commit vendor
This commit is contained in:
1
vendor/laminas/laminas-mail/COPYRIGHT.md
vendored
Normal file
1
vendor/laminas/laminas-mail/COPYRIGHT.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC. (https://getlaminas.org/)
|
||||
26
vendor/laminas/laminas-mail/LICENSE.md
vendored
Normal file
26
vendor/laminas/laminas-mail/LICENSE.md
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Laminas Foundation nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
166
vendor/laminas/laminas-mail/src/Address.php
vendored
Normal file
166
vendor/laminas/laminas-mail/src/Address.php
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
use Laminas\Validator\EmailAddress as EmailAddressValidator;
|
||||
use Laminas\Validator\Hostname;
|
||||
|
||||
class Address implements Address\AddressInterface
|
||||
{
|
||||
protected $comment;
|
||||
protected $email;
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Create an instance from a string value.
|
||||
*
|
||||
* Parses a string representing a single address. If it is a valid format,
|
||||
* it then creates and returns an instance of itself using the name and
|
||||
* email it has parsed from the value.
|
||||
*
|
||||
* @param string $address
|
||||
* @param null|string $comment Comment associated with the address, if any.
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return self
|
||||
*/
|
||||
public static function fromString($address, $comment = null)
|
||||
{
|
||||
if (! preg_match('/^((?P<name>.*)<(?P<namedEmail>[^>]+)>|(?P<email>.+))$/', $address, $matches)) {
|
||||
throw new Exception\InvalidArgumentException('Invalid address format');
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if (isset($matches['name'])) {
|
||||
$name = trim($matches['name']);
|
||||
}
|
||||
if (empty($name)) {
|
||||
$name = null;
|
||||
}
|
||||
|
||||
if (isset($matches['namedEmail'])) {
|
||||
$email = $matches['namedEmail'];
|
||||
}
|
||||
if (isset($matches['email'])) {
|
||||
$email = $matches['email'];
|
||||
}
|
||||
$email = trim($email);
|
||||
|
||||
return new static($email, $name, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $email
|
||||
* @param null|string $name
|
||||
* @param null|string $comment
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($email, $name = null, $comment = null)
|
||||
{
|
||||
$emailAddressValidator = new EmailAddressValidator(Hostname::ALLOW_DNS | Hostname::ALLOW_LOCAL);
|
||||
if (! is_string($email) || empty($email)) {
|
||||
throw new Exception\InvalidArgumentException('Email must be a valid email address');
|
||||
}
|
||||
|
||||
if (preg_match("/[\r\n]/", $email)) {
|
||||
throw new Exception\InvalidArgumentException('CRLF injection detected');
|
||||
}
|
||||
|
||||
if (! $emailAddressValidator->isValid($email)) {
|
||||
$invalidMessages = $emailAddressValidator->getMessages();
|
||||
throw new Exception\InvalidArgumentException(array_shift($invalidMessages));
|
||||
}
|
||||
|
||||
if (null !== $name) {
|
||||
if (! is_string($name)) {
|
||||
throw new Exception\InvalidArgumentException('Name must be a string');
|
||||
}
|
||||
|
||||
if (preg_match("/[\r\n]/", $name)) {
|
||||
throw new Exception\InvalidArgumentException('CRLF injection detected');
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
$this->email = $email;
|
||||
|
||||
if (null !== $comment) {
|
||||
$this->comment = $comment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve email
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve comment, if any
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
$string = sprintf('<%s>', $this->getEmail());
|
||||
$name = $this->constructName();
|
||||
if (null === $name) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return sprintf('%s %s', $name, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the name string
|
||||
*
|
||||
* If a comment is present, appends the comment (commented using parens) to
|
||||
* the name before returning it; otherwise, returns just the name.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
private function constructName()
|
||||
{
|
||||
$name = $this->getName();
|
||||
$comment = $this->getComment();
|
||||
|
||||
if ($comment === null || $comment === '') {
|
||||
return $name;
|
||||
}
|
||||
|
||||
$string = sprintf('%s (%s)', $name, $comment);
|
||||
return trim($string);
|
||||
}
|
||||
}
|
||||
33
vendor/laminas/laminas-mail/src/Address/AddressInterface.php
vendored
Normal file
33
vendor/laminas/laminas-mail/src/Address/AddressInterface.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Address;
|
||||
|
||||
interface AddressInterface
|
||||
{
|
||||
/**
|
||||
* Retrieve email
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail();
|
||||
|
||||
/**
|
||||
* Retrieve name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* String representation of address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString();
|
||||
}
|
||||
236
vendor/laminas/laminas-mail/src/AddressList.php
vendored
Normal file
236
vendor/laminas/laminas-mail/src/AddressList.php
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
use Countable;
|
||||
use Iterator;
|
||||
|
||||
class AddressList implements Countable, Iterator
|
||||
{
|
||||
/**
|
||||
* List of Address objects we're managing
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $addresses = [];
|
||||
|
||||
/**
|
||||
* Add an address to the list
|
||||
*
|
||||
* @param string|Address\AddressInterface $emailOrAddress
|
||||
* @param null|string $name
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return AddressList
|
||||
*/
|
||||
public function add($emailOrAddress, $name = null)
|
||||
{
|
||||
if (is_string($emailOrAddress)) {
|
||||
$emailOrAddress = $this->createAddress($emailOrAddress, $name);
|
||||
}
|
||||
|
||||
if (! $emailOrAddress instanceof Address\AddressInterface) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects an email address or %s\Address object as its first argument; received "%s"',
|
||||
__METHOD__,
|
||||
__NAMESPACE__,
|
||||
(is_object($emailOrAddress) ? get_class($emailOrAddress) : gettype($emailOrAddress))
|
||||
));
|
||||
}
|
||||
|
||||
$email = strtolower($emailOrAddress->getEmail());
|
||||
if ($this->has($email)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->addresses[$email] = $emailOrAddress;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add many addresses at once
|
||||
*
|
||||
* If an email key is provided, it will be used as the email, and the value
|
||||
* as the name. Otherwise, the value is passed as the sole argument to add(),
|
||||
* and, as such, can be either email strings or Address\AddressInterface objects.
|
||||
*
|
||||
* @param array $addresses
|
||||
* @throws Exception\RuntimeException
|
||||
* @return AddressList
|
||||
*/
|
||||
public function addMany(array $addresses)
|
||||
{
|
||||
foreach ($addresses as $key => $value) {
|
||||
if (is_int($key) || is_numeric($key)) {
|
||||
$this->add($value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_string($key)) {
|
||||
throw new Exception\RuntimeException(sprintf(
|
||||
'Invalid key type in provided addresses array ("%s")',
|
||||
(is_object($key) ? get_class($key) : var_export($key, 1))
|
||||
));
|
||||
}
|
||||
|
||||
$this->add($key, $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an address to the list from any valid string format, such as
|
||||
* - "Laminas Dev" <dev@laminas.com>
|
||||
* - dev@laminas.com
|
||||
*
|
||||
* @param string $address
|
||||
* @param null|string $comment Comment associated with the address, if any.
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return AddressList
|
||||
*/
|
||||
public function addFromString($address, $comment = null)
|
||||
{
|
||||
$this->add(Address::fromString($address, $comment));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge another address list into this one
|
||||
*
|
||||
* @param AddressList $addressList
|
||||
* @return AddressList
|
||||
*/
|
||||
public function merge(AddressList $addressList)
|
||||
{
|
||||
foreach ($addressList as $address) {
|
||||
$this->add($address);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the email exist in this list?
|
||||
*
|
||||
* @param string $email
|
||||
* @return bool
|
||||
*/
|
||||
public function has($email)
|
||||
{
|
||||
$email = strtolower($email);
|
||||
return isset($this->addresses[$email]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an address by email
|
||||
*
|
||||
* @param string $email
|
||||
* @return bool|Address\AddressInterface
|
||||
*/
|
||||
public function get($email)
|
||||
{
|
||||
$email = strtolower($email);
|
||||
if (! isset($this->addresses[$email])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->addresses[$email];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an address from the list
|
||||
*
|
||||
* @param string $email
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($email)
|
||||
{
|
||||
$email = strtolower($email);
|
||||
if (! isset($this->addresses[$email])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($this->addresses[$email]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return count of addresses
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind iterator
|
||||
*
|
||||
* @return mixed the value of the first addresses element, or false if the addresses is
|
||||
* empty.
|
||||
* @see addresses
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
return reset($this->addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current item in iteration
|
||||
*
|
||||
* @return Address
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return key of current item of iteration
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to next item
|
||||
*
|
||||
* @return mixed the addresses value in the next place that's pointed to by the
|
||||
* internal array pointer, or false if there are no more elements.
|
||||
* @see addresses
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current item of iteration valid?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
$key = key($this->addresses);
|
||||
return ($key !== null && $key !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an address object
|
||||
*
|
||||
* @param string $email
|
||||
* @param string|null $name
|
||||
* @return Address
|
||||
*/
|
||||
protected function createAddress($email, $name)
|
||||
{
|
||||
return new Address($email, $name);
|
||||
}
|
||||
}
|
||||
42
vendor/laminas/laminas-mail/src/ConfigProvider.php
vendored
Normal file
42
vendor/laminas/laminas-mail/src/ConfigProvider.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
/**
|
||||
* Retrieve configuration for laminas-mail package.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return [
|
||||
'dependencies' => $this->getDependencyConfig(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve dependency settings for laminas-mail package.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencyConfig()
|
||||
{
|
||||
return [
|
||||
// Legacy Zend Framework aliases
|
||||
'aliases' => [
|
||||
\Zend\Mail\Protocol\SmtpPluginManager::class => Protocol\SmtpPluginManager::class,
|
||||
],
|
||||
'factories' => [
|
||||
Protocol\SmtpPluginManager::class => Protocol\SmtpPluginManagerFactory::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
17
vendor/laminas/laminas-mail/src/Exception/BadMethodCallException.php
vendored
Normal file
17
vendor/laminas/laminas-mail/src/Exception/BadMethodCallException.php
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class BadMethodCallException extends \BadMethodCallException implements
|
||||
ExceptionInterface
|
||||
{
|
||||
}
|
||||
16
vendor/laminas/laminas-mail/src/Exception/DomainException.php
vendored
Normal file
16
vendor/laminas/laminas-mail/src/Exception/DomainException.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class DomainException extends \DomainException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
13
vendor/laminas/laminas-mail/src/Exception/ExceptionInterface.php
vendored
Normal file
13
vendor/laminas/laminas-mail/src/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Exception;
|
||||
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
||||
17
vendor/laminas/laminas-mail/src/Exception/InvalidArgumentException.php
vendored
Normal file
17
vendor/laminas/laminas-mail/src/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements
|
||||
ExceptionInterface
|
||||
{
|
||||
}
|
||||
16
vendor/laminas/laminas-mail/src/Exception/OutOfBoundsException.php
vendored
Normal file
16
vendor/laminas/laminas-mail/src/Exception/OutOfBoundsException.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
16
vendor/laminas/laminas-mail/src/Exception/RuntimeException.php
vendored
Normal file
16
vendor/laminas/laminas-mail/src/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
261
vendor/laminas/laminas-mail/src/Header/AbstractAddressList.php
vendored
Normal file
261
vendor/laminas/laminas-mail/src/Header/AbstractAddressList.php
vendored
Normal file
@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mail\Address;
|
||||
use Laminas\Mail\AddressList;
|
||||
use Laminas\Mail\Headers;
|
||||
use TrueBV\Exception\OutOfBoundsException;
|
||||
use TrueBV\Punycode;
|
||||
|
||||
/**
|
||||
* Base class for headers composing address lists (to, from, cc, bcc, reply-to)
|
||||
*/
|
||||
abstract class AbstractAddressList implements HeaderInterface
|
||||
{
|
||||
/**
|
||||
* @var AddressList
|
||||
*/
|
||||
protected $addressList;
|
||||
|
||||
/**
|
||||
* @var string Normalized field name
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* Header encoding
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $encoding = 'ASCII';
|
||||
|
||||
/**
|
||||
* @var string lower case field name
|
||||
*/
|
||||
protected static $type;
|
||||
|
||||
/**
|
||||
* @var Punycode|null
|
||||
*/
|
||||
private static $punycode;
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
|
||||
if (strtolower($fieldName) !== static::$type) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'Invalid header line for "%s" string',
|
||||
__CLASS__
|
||||
));
|
||||
}
|
||||
|
||||
// split value on ","
|
||||
$fieldValue = str_replace(Headers::FOLDING, ' ', $fieldValue);
|
||||
$fieldValue = preg_replace('/[^:]+:([^;]*);/', '$1,', $fieldValue);
|
||||
$values = ListParser::parse($fieldValue);
|
||||
|
||||
$wasEncoded = false;
|
||||
$addresses = array_map(
|
||||
function ($value) use (&$wasEncoded) {
|
||||
$decodedValue = HeaderWrap::mimeDecodeValue($value);
|
||||
$wasEncoded = $wasEncoded || ($decodedValue !== $value);
|
||||
|
||||
$value = trim($decodedValue);
|
||||
|
||||
$comments = self::getComments($value);
|
||||
$value = self::stripComments($value);
|
||||
|
||||
$value = preg_replace(
|
||||
[
|
||||
'#(?<!\\\)"(.*)(?<!\\\)"#', // quoted-text
|
||||
'#\\\([\x01-\x09\x0b\x0c\x0e-\x7f])#', // quoted-pair
|
||||
],
|
||||
[
|
||||
'\\1',
|
||||
'\\1',
|
||||
],
|
||||
$value
|
||||
);
|
||||
|
||||
return empty($value) ? null : Address::fromString($value, $comments);
|
||||
},
|
||||
$values
|
||||
);
|
||||
$addresses = array_filter($addresses);
|
||||
|
||||
$header = new static();
|
||||
if ($wasEncoded) {
|
||||
$header->setEncoding('UTF-8');
|
||||
}
|
||||
|
||||
/** @var AddressList $addressList */
|
||||
$addressList = $header->getAddressList();
|
||||
foreach ($addresses as $address) {
|
||||
$addressList->add($address);
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return $this->fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely convert UTF-8 encoded domain name to ASCII
|
||||
* @param string $domainName the UTF-8 encoded email
|
||||
* @return string
|
||||
*/
|
||||
protected function idnToAscii($domainName)
|
||||
{
|
||||
if (null === self::$punycode) {
|
||||
self::$punycode = new Punycode();
|
||||
}
|
||||
try {
|
||||
return self::$punycode->encode($domainName);
|
||||
} catch (OutOfBoundsException $e) {
|
||||
return $domainName;
|
||||
}
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
$emails = [];
|
||||
$encoding = $this->getEncoding();
|
||||
|
||||
foreach ($this->getAddressList() as $address) {
|
||||
$email = $address->getEmail();
|
||||
$name = $address->getName();
|
||||
|
||||
// quote $name if value requires so
|
||||
if (! empty($name) && (false !== strpos($name, ',') || false !== strpos($name, ';'))) {
|
||||
// FIXME: what if name contains double quote?
|
||||
$name = sprintf('"%s"', $name);
|
||||
}
|
||||
|
||||
if ($format === HeaderInterface::FORMAT_ENCODED
|
||||
&& 'ASCII' !== $encoding
|
||||
) {
|
||||
if (! empty($name)) {
|
||||
$name = HeaderWrap::mimeEncodeValue($name, $encoding);
|
||||
}
|
||||
|
||||
if (preg_match('/^(.+)@([^@]+)$/', $email, $matches)) {
|
||||
$localPart = $matches[1];
|
||||
$hostname = $this->idnToAscii($matches[2]);
|
||||
$email = sprintf('%s@%s', $localPart, $hostname);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($name)) {
|
||||
$emails[] = $email;
|
||||
} else {
|
||||
$emails[] = sprintf('%s <%s>', $name, $email);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the values are valid before sending them.
|
||||
if ($format !== HeaderInterface::FORMAT_RAW) {
|
||||
foreach ($emails as $email) {
|
||||
HeaderValue::assertValid($email);
|
||||
}
|
||||
}
|
||||
|
||||
return implode(',' . Headers::FOLDING, $emails);
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set address list for this header
|
||||
*
|
||||
* @param AddressList $addressList
|
||||
*/
|
||||
public function setAddressList(AddressList $addressList)
|
||||
{
|
||||
$this->addressList = $addressList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get address list managed by this header
|
||||
*
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getAddressList()
|
||||
{
|
||||
if (null === $this->addressList) {
|
||||
$this->setAddressList(new AddressList());
|
||||
}
|
||||
return $this->addressList;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
$name = $this->getFieldName();
|
||||
$value = $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
return (empty($value)) ? '' : sprintf('%s: %s', $name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve comments from value, if any.
|
||||
*
|
||||
* Supposed to be private, protected as a workaround for PHP bug 68194
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected static function getComments($value)
|
||||
{
|
||||
$matches = [];
|
||||
preg_match_all(
|
||||
'/\\(
|
||||
(?P<comment>(
|
||||
\\\\.|
|
||||
[^\\\\)]
|
||||
)+)
|
||||
\\)/x',
|
||||
$value,
|
||||
$matches
|
||||
);
|
||||
return isset($matches['comment']) ? implode(', ', $matches['comment']) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip all comments from value, if any.
|
||||
*
|
||||
* Supposed to be private, protected as a workaround for PHP bug 68194
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected static function stripComments($value)
|
||||
{
|
||||
return preg_replace(
|
||||
'/\\(
|
||||
(
|
||||
\\\\.|
|
||||
[^\\\\)]
|
||||
)+
|
||||
\\)/x',
|
||||
'',
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
22
vendor/laminas/laminas-mail/src/Header/Bcc.php
vendored
Normal file
22
vendor/laminas/laminas-mail/src/Header/Bcc.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class Bcc extends AbstractAddressList
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'Bcc';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $type = 'bcc';
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/Cc.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/Cc.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class Cc extends AbstractAddressList
|
||||
{
|
||||
protected $fieldName = 'Cc';
|
||||
protected static $type = 'cc';
|
||||
}
|
||||
296
vendor/laminas/laminas-mail/src/Header/ContentDisposition.php
vendored
Normal file
296
vendor/laminas/laminas-mail/src/Header/ContentDisposition.php
vendored
Normal file
@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mail\Headers;
|
||||
use Laminas\Mime\Mime;
|
||||
|
||||
class ContentDisposition implements UnstructuredInterface
|
||||
{
|
||||
/**
|
||||
* 78 chars (RFC 2822) - (semicolon + space (Header::FOLDING))
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAX_PARAMETER_LENGTH = 76;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $disposition = 'inline';
|
||||
|
||||
/**
|
||||
* Header encoding
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $encoding = 'ASCII';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $parameters = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'content-disposition') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for Content-Disposition string');
|
||||
}
|
||||
|
||||
$value = str_replace(Headers::FOLDING, ' ', $value);
|
||||
$parts = explode(';', $value, 2);
|
||||
|
||||
$header = new static();
|
||||
$header->setDisposition($parts[0]);
|
||||
|
||||
if (isset($parts[1])) {
|
||||
$values = ListParser::parse(trim($parts[1]), [';', '=']);
|
||||
$length = count($values);
|
||||
$continuedValues = [];
|
||||
|
||||
for ($i = 0; $i < $length; $i += 2) {
|
||||
$value = $values[$i + 1];
|
||||
$value = trim($value, "'\" \t\n\r\0\x0B");
|
||||
$name = trim($values[$i], "'\" \t\n\r\0\x0B");
|
||||
|
||||
if (strpos($name, '*')) {
|
||||
list($name, $count) = explode('*', $name);
|
||||
if (! isset($continuedValues[$name])) {
|
||||
$continuedValues[$name] = [];
|
||||
}
|
||||
$continuedValues[$name][$count] = $value;
|
||||
} else {
|
||||
$header->setParameter($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($continuedValues as $name => $values) {
|
||||
$value = '';
|
||||
for ($i = 0; $i < count($values); $i++) {
|
||||
if (! isset($values[$i])) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'Invalid header line for Content-Disposition string - incomplete continuation'
|
||||
);
|
||||
}
|
||||
$value .= $values[$i];
|
||||
}
|
||||
$header->setParameter($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Content-Disposition';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
$result = $this->disposition;
|
||||
if (empty($this->parameters)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ($this->parameters as $attribute => $value) {
|
||||
$valueIsEncoded = false;
|
||||
if (HeaderInterface::FORMAT_ENCODED === $format && ! Mime::isPrintable($value)) {
|
||||
$value = $this->getEncodedValue($value);
|
||||
$valueIsEncoded = true;
|
||||
}
|
||||
|
||||
$line = sprintf('%s="%s"', $attribute, $value);
|
||||
|
||||
if (strlen($line) < self::MAX_PARAMETER_LENGTH) {
|
||||
$lines = explode(Headers::FOLDING, $result);
|
||||
|
||||
if (count($lines) === 1) {
|
||||
$existingLineLength = strlen('Content-Disposition: ' . $result);
|
||||
} else {
|
||||
$existingLineLength = 1 + strlen($lines[count($lines) - 1]);
|
||||
}
|
||||
|
||||
if ((2 + $existingLineLength + strlen($line)) <= self::MAX_PARAMETER_LENGTH) {
|
||||
$result .= '; ' . $line;
|
||||
} else {
|
||||
$result .= ';' . Headers::FOLDING . $line;
|
||||
}
|
||||
} else {
|
||||
// Use 'continuation' per RFC 2231
|
||||
$maxValueLength = strlen($value);
|
||||
do {
|
||||
$maxValueLength = ceil(0.6 * $maxValueLength);
|
||||
} while ($maxValueLength > self::MAX_PARAMETER_LENGTH);
|
||||
|
||||
if ($valueIsEncoded) {
|
||||
$encodedLength = strlen($value);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
$decodedLength = strlen($value);
|
||||
$maxValueLength -= ($encodedLength - $decodedLength);
|
||||
}
|
||||
|
||||
$valueParts = str_split($value, $maxValueLength);
|
||||
$i = 0;
|
||||
foreach ($valueParts as $valuePart) {
|
||||
$attributePart = $attribute . '*' . $i++;
|
||||
if ($valueIsEncoded) {
|
||||
$valuePart = $this->getEncodedValue($valuePart);
|
||||
}
|
||||
$result .= sprintf(';%s%s="%s"', Headers::FOLDING, $attributePart, $valuePart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function getEncodedValue($value)
|
||||
{
|
||||
$configuredEncoding = $this->encoding;
|
||||
$this->encoding = 'UTF-8';
|
||||
$value = HeaderWrap::wrap($value, $this);
|
||||
$this->encoding = $configuredEncoding;
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getEncoding()
|
||||
{
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
return 'Content-Disposition: ' . $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content disposition
|
||||
* Expected values include 'inline', 'attachment'
|
||||
*
|
||||
* @param string $disposition
|
||||
* @return ContentDisposition
|
||||
*/
|
||||
public function setDisposition($disposition)
|
||||
{
|
||||
$this->disposition = strtolower($disposition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content disposition
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisposition()
|
||||
{
|
||||
return $this->disposition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a parameter pair
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return ContentDisposition
|
||||
*/
|
||||
public function setParameter($name, $value)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
if (! HeaderValue::isValid($name)) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'Invalid content-disposition parameter name detected'
|
||||
);
|
||||
}
|
||||
// '5' here is for the quotes & equal sign in `name="value"`,
|
||||
// and the space & semicolon for line folding
|
||||
if ((strlen($name) + 5) >= self::MAX_PARAMETER_LENGTH) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'Invalid content-disposition parameter name detected (too long)'
|
||||
);
|
||||
}
|
||||
|
||||
$this->parameters[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all parameters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parameter by name
|
||||
*
|
||||
* @param string $name
|
||||
* @return null|string
|
||||
*/
|
||||
public function getParameter($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
if (isset($this->parameters[$name])) {
|
||||
return $this->parameters[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a named parameter
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function removeParameter($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
if (isset($this->parameters[$name])) {
|
||||
unset($this->parameters[$name]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
114
vendor/laminas/laminas-mail/src/Header/ContentTransferEncoding.php
vendored
Normal file
114
vendor/laminas/laminas-mail/src/Header/ContentTransferEncoding.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class ContentTransferEncoding implements HeaderInterface
|
||||
{
|
||||
/**
|
||||
* Allowed Content-Transfer-Encoding parameters specified by RFC 1521
|
||||
* (reduced set)
|
||||
* @var array
|
||||
*/
|
||||
protected static $allowedTransferEncodings = [
|
||||
'7bit',
|
||||
'8bit',
|
||||
'quoted-printable',
|
||||
'base64',
|
||||
'binary',
|
||||
/*
|
||||
* not implemented:
|
||||
* x-token: 'X-'
|
||||
*/
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $transferEncoding;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $parameters = [];
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'content-transfer-encoding') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for Content-Transfer-Encoding string');
|
||||
}
|
||||
|
||||
$header = new static();
|
||||
$header->setTransferEncoding($value);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Content-Transfer-Encoding';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
return $this->transferEncoding;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
// Header must be always in US-ASCII
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
return 'ASCII';
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'Content-Transfer-Encoding: ' . $this->getFieldValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content transfer encoding
|
||||
*
|
||||
* @param string $transferEncoding
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return $this
|
||||
*/
|
||||
public function setTransferEncoding($transferEncoding)
|
||||
{
|
||||
// Per RFC 1521, the value of the header is not case sensitive
|
||||
$transferEncoding = strtolower($transferEncoding);
|
||||
|
||||
if (! in_array($transferEncoding, static::$allowedTransferEncodings)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects one of "'. implode(', ', static::$allowedTransferEncodings) . '"; received "%s"',
|
||||
__METHOD__,
|
||||
(string) $transferEncoding
|
||||
));
|
||||
}
|
||||
$this->transferEncoding = $transferEncoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content transfer encoding
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTransferEncoding()
|
||||
{
|
||||
return $this->transferEncoding;
|
||||
}
|
||||
}
|
||||
202
vendor/laminas/laminas-mail/src/Header/ContentType.php
vendored
Normal file
202
vendor/laminas/laminas-mail/src/Header/ContentType.php
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mail\Headers;
|
||||
use Laminas\Mime\Mime;
|
||||
|
||||
class ContentType implements UnstructuredInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Header encoding
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $encoding = 'ASCII';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $parameters = [];
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'content-type') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for Content-Type string');
|
||||
}
|
||||
|
||||
$value = str_replace(Headers::FOLDING, ' ', $value);
|
||||
$parts = explode(';', $value, 2);
|
||||
|
||||
$header = new static();
|
||||
$header->setType($parts[0]);
|
||||
|
||||
if (isset($parts[1])) {
|
||||
$values = ListParser::parse(trim($parts[1]), [';', '=']);
|
||||
$length = count($values);
|
||||
|
||||
for ($i = 0; $i < $length; $i += 2) {
|
||||
$value = $values[$i + 1];
|
||||
$value = trim($value, "'\" \t\n\r\0\x0B");
|
||||
$header->addParameter($values[$i], $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Content-Type';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
$prepared = $this->type;
|
||||
if (empty($this->parameters)) {
|
||||
return $prepared;
|
||||
}
|
||||
|
||||
$values = [$prepared];
|
||||
foreach ($this->parameters as $attribute => $value) {
|
||||
if (HeaderInterface::FORMAT_ENCODED === $format && ! Mime::isPrintable($value)) {
|
||||
$this->encoding = 'UTF-8';
|
||||
$value = HeaderWrap::wrap($value, $this);
|
||||
$this->encoding = 'ASCII';
|
||||
}
|
||||
|
||||
$values[] = sprintf('%s="%s"', $attribute, $value);
|
||||
}
|
||||
|
||||
return implode(';' . Headers::FOLDING, $values);
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'Content-Type: ' . $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the content type
|
||||
*
|
||||
* @param string $type
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return ContentType
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
if (! preg_match('/^[a-z-]+\/[a-z0-9.+-]+$/i', $type)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a value in the format "type/subtype"; received "%s"',
|
||||
__METHOD__,
|
||||
(string) $type
|
||||
));
|
||||
}
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a parameter pair
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return ContentType
|
||||
* @throws Exception\InvalidArgumentException for parameter names that do not follow RFC 2822
|
||||
* @throws Exception\InvalidArgumentException for parameter values that do not follow RFC 2822
|
||||
*/
|
||||
public function addParameter($name, $value)
|
||||
{
|
||||
$name = trim(strtolower($name));
|
||||
$value = (string) $value;
|
||||
|
||||
if (! HeaderValue::isValid($name)) {
|
||||
throw new Exception\InvalidArgumentException('Invalid content-type parameter name detected');
|
||||
}
|
||||
if (! HeaderWrap::canBeEncoded($value)) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'Parameter value must be composed of printable US-ASCII or UTF-8 characters.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->parameters[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all parameters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a parameter by name
|
||||
*
|
||||
* @param string $name
|
||||
* @return null|string
|
||||
*/
|
||||
public function getParameter($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
if (isset($this->parameters[$name])) {
|
||||
return $this->parameters[$name];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a named parameter
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function removeParameter($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
if (isset($this->parameters[$name])) {
|
||||
unset($this->parameters[$name]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
69
vendor/laminas/laminas-mail/src/Header/Date.php
vendored
Normal file
69
vendor/laminas/laminas-mail/src/Header/Date.php
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
/**
|
||||
* @todo Add accessors for setting date from DateTime, Laminas\Date, or a string
|
||||
*/
|
||||
class Date implements HeaderInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'date') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for Date string');
|
||||
}
|
||||
|
||||
$header = new static($value);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
if (! HeaderValue::isValid($value)) {
|
||||
throw new Exception\InvalidArgumentException('Invalid Date header value detected');
|
||||
}
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Date';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
// This header must be always in US-ASCII
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
return 'ASCII';
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'Date: ' . $this->getFieldValue();
|
||||
}
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/Exception/BadMethodCallException.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/Exception/BadMethodCallException.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
class BadMethodCallException extends Exception\BadMethodCallException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/Exception/ExceptionInterface.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header\Exception;
|
||||
|
||||
use Laminas\Mail\Exception\ExceptionInterface as MailException;
|
||||
|
||||
interface ExceptionInterface extends MailException
|
||||
{
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/Exception/InvalidArgumentException.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
class InvalidArgumentException extends Exception\InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/Exception/RuntimeException.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
class RuntimeException extends Exception\RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/From.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/From.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class From extends AbstractAddressList
|
||||
{
|
||||
protected $fieldName = 'From';
|
||||
protected static $type = 'from';
|
||||
}
|
||||
198
vendor/laminas/laminas-mail/src/Header/GenericHeader.php
vendored
Normal file
198
vendor/laminas/laminas-mail/src/Header/GenericHeader.php
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mime\Mime;
|
||||
|
||||
class GenericHeader implements HeaderInterface, UnstructuredInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldValue = null;
|
||||
|
||||
/**
|
||||
* Header encoding
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $encoding;
|
||||
|
||||
/**
|
||||
* @param string $headerLine
|
||||
* @return GenericHeader
|
||||
*/
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = self::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
$header = new static($name, $value);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the header line in `name` and `value` parts.
|
||||
*
|
||||
* @param string $headerLine
|
||||
* @return string[] `name` in the first index and `value` in the second.
|
||||
* @throws Exception\InvalidArgumentException If header does not match with the format ``name:value``
|
||||
*/
|
||||
public static function splitHeaderLine($headerLine)
|
||||
{
|
||||
$parts = explode(':', $headerLine, 2);
|
||||
if (count($parts) !== 2) {
|
||||
throw new Exception\InvalidArgumentException('Header must match with the format "name:value"');
|
||||
}
|
||||
|
||||
if (! HeaderName::isValid($parts[0])) {
|
||||
throw new Exception\InvalidArgumentException('Invalid header name detected');
|
||||
}
|
||||
|
||||
if (! HeaderValue::isValid($parts[1])) {
|
||||
throw new Exception\InvalidArgumentException('Invalid header value detected');
|
||||
}
|
||||
|
||||
$parts[1] = ltrim($parts[1]);
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $fieldName Optional
|
||||
* @param string $fieldValue Optional
|
||||
*/
|
||||
public function __construct($fieldName = null, $fieldValue = null)
|
||||
{
|
||||
if ($fieldName) {
|
||||
$this->setFieldName($fieldName);
|
||||
}
|
||||
|
||||
if ($fieldValue !== null) {
|
||||
$this->setFieldValue($fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set header name
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return GenericHeader
|
||||
* @throws Exception\InvalidArgumentException;
|
||||
*/
|
||||
public function setFieldName($fieldName)
|
||||
{
|
||||
if (! is_string($fieldName) || empty($fieldName)) {
|
||||
throw new Exception\InvalidArgumentException('Header name must be a string');
|
||||
}
|
||||
|
||||
// Pre-filter to normalize valid characters, change underscore to dash
|
||||
$fieldName = str_replace(' ', '-', ucwords(str_replace(['_', '-'], ' ', $fieldName)));
|
||||
|
||||
if (! HeaderName::isValid($fieldName)) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'Header name must be composed of printable US-ASCII characters, except colon.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->fieldName = $fieldName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return $this->fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set header value
|
||||
*
|
||||
* @param string $fieldValue
|
||||
* @return GenericHeader
|
||||
* @throws Exception\InvalidArgumentException;
|
||||
*/
|
||||
public function setFieldValue($fieldValue)
|
||||
{
|
||||
$fieldValue = (string) $fieldValue;
|
||||
|
||||
if (! HeaderWrap::canBeEncoded($fieldValue)) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'Header value must be composed of printable US-ASCII characters and valid folding sequences.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->fieldValue = $fieldValue;
|
||||
$this->encoding = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
if (HeaderInterface::FORMAT_ENCODED === $format) {
|
||||
return HeaderWrap::wrap($this->fieldValue, $this);
|
||||
}
|
||||
|
||||
return $this->fieldValue;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
if ($encoding === $this->encoding) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($encoding === null) {
|
||||
$this->encoding = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$encoding = strtoupper($encoding);
|
||||
if ($encoding === 'UTF-8') {
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($encoding === 'ASCII' && Mime::isPrintable($this->fieldValue)) {
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->encoding = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
if (! $this->encoding) {
|
||||
$this->encoding = Mime::isPrintable($this->fieldValue) ? 'ASCII' : 'UTF-8';
|
||||
}
|
||||
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
$name = $this->getFieldName();
|
||||
if (empty($name)) {
|
||||
throw new Exception\RuntimeException('Header name is not set, use setFieldName()');
|
||||
}
|
||||
$value = $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
|
||||
return $name . ': ' . $value;
|
||||
}
|
||||
}
|
||||
55
vendor/laminas/laminas-mail/src/Header/GenericMultiHeader.php
vendored
Normal file
55
vendor/laminas/laminas-mail/src/Header/GenericMultiHeader.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
/**
|
||||
* Generic class for Headers with multiple occurs in the same message
|
||||
*/
|
||||
class GenericMultiHeader extends GenericHeader implements MultipleHeadersInterface
|
||||
{
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$fieldValue = HeaderWrap::mimeDecodeValue($fieldValue);
|
||||
|
||||
if (strpos($fieldValue, ',')) {
|
||||
$headers = [];
|
||||
foreach (explode(',', $fieldValue) as $multiValue) {
|
||||
$headers[] = new static($fieldName, $multiValue);
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
return new static($fieldName, $fieldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast multiple header objects to a single string header
|
||||
*
|
||||
* @param array $headers
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return string
|
||||
*/
|
||||
public function toStringMultipleHeaders(array $headers)
|
||||
{
|
||||
$name = $this->getFieldName();
|
||||
$values = [$this->getFieldValue(HeaderInterface::FORMAT_ENCODED)];
|
||||
|
||||
foreach ($headers as $header) {
|
||||
if (! $header instanceof static) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'This method toStringMultipleHeaders was expecting an array of headers of the same type'
|
||||
);
|
||||
}
|
||||
$values[] = $header->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
}
|
||||
|
||||
return $name . ': ' . implode(',', $values);
|
||||
}
|
||||
}
|
||||
75
vendor/laminas/laminas-mail/src/Header/HeaderInterface.php
vendored
Normal file
75
vendor/laminas/laminas-mail/src/Header/HeaderInterface.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
interface HeaderInterface
|
||||
{
|
||||
/**
|
||||
* Format value in Mime-Encoding (Quoted-Printable). Result is valid US-ASCII string
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
const FORMAT_ENCODED = true;
|
||||
|
||||
/**
|
||||
* Return value in internal encoding which is usually UTF-8
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
const FORMAT_RAW = false;
|
||||
|
||||
/**
|
||||
* Factory to generate a header object from a string
|
||||
*
|
||||
* @param string $headerLine
|
||||
* @return static
|
||||
* @throws Exception\InvalidArgumentException If the header does not match with RFC 2822 definition.
|
||||
* @see http://tools.ietf.org/html/rfc2822#section-2.2
|
||||
*/
|
||||
public static function fromString($headerLine);
|
||||
|
||||
/**
|
||||
* Retrieve header name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFieldName();
|
||||
|
||||
/**
|
||||
* Retrieve header value
|
||||
*
|
||||
* @param bool $format Return the value in Mime::Encoded or in Raw format
|
||||
* @return string
|
||||
*/
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW);
|
||||
|
||||
/**
|
||||
* Set header encoding
|
||||
*
|
||||
* @param string $encoding
|
||||
* @return $this
|
||||
*/
|
||||
public function setEncoding($encoding);
|
||||
|
||||
/**
|
||||
* Get header encoding
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncoding();
|
||||
|
||||
/**
|
||||
* Cast to string
|
||||
*
|
||||
* Returns in form of "NAME: VALUE"
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString();
|
||||
}
|
||||
49
vendor/laminas/laminas-mail/src/Header/HeaderLoader.php
vendored
Normal file
49
vendor/laminas/laminas-mail/src/Header/HeaderLoader.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Loader\PluginClassLoader;
|
||||
|
||||
/**
|
||||
* Plugin Class Loader implementation for HTTP headers
|
||||
*/
|
||||
class HeaderLoader extends PluginClassLoader
|
||||
{
|
||||
/**
|
||||
* @var array Pre-aliased Header plugins
|
||||
*/
|
||||
protected $plugins = [
|
||||
'bcc' => Bcc::class,
|
||||
'cc' => Cc::class,
|
||||
'contentdisposition' => ContentDisposition::class,
|
||||
'content_disposition' => ContentDisposition::class,
|
||||
'content-disposition' => ContentDisposition::class,
|
||||
'contenttype' => ContentType::class,
|
||||
'content_type' => ContentType::class,
|
||||
'content-type' => ContentType::class,
|
||||
'contenttransferencoding' => ContentTransferEncoding::class,
|
||||
'content_transfer_encoding' => ContentTransferEncoding::class,
|
||||
'content-transfer-encoding' => ContentTransferEncoding::class,
|
||||
'date' => Date::class,
|
||||
'from' => From::class,
|
||||
'in-reply-to' => InReplyTo::class,
|
||||
'message-id' => MessageId::class,
|
||||
'mimeversion' => MimeVersion::class,
|
||||
'mime_version' => MimeVersion::class,
|
||||
'mime-version' => MimeVersion::class,
|
||||
'received' => Received::class,
|
||||
'references' => References::class,
|
||||
'replyto' => ReplyTo::class,
|
||||
'reply_to' => ReplyTo::class,
|
||||
'reply-to' => ReplyTo::class,
|
||||
'sender' => Sender::class,
|
||||
'subject' => Subject::class,
|
||||
'to' => To::class,
|
||||
];
|
||||
}
|
||||
75
vendor/laminas/laminas-mail/src/Header/HeaderLocator.php
vendored
Normal file
75
vendor/laminas/laminas-mail/src/Header/HeaderLocator.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
/**
|
||||
* Plugin Class Loader implementation for HTTP headers
|
||||
*/
|
||||
final class HeaderLocator implements HeaderLocatorInterface
|
||||
{
|
||||
/**
|
||||
* @var array Pre-aliased Header plugins
|
||||
*/
|
||||
private $plugins = [
|
||||
'bcc' => Bcc::class,
|
||||
'cc' => Cc::class,
|
||||
'contentdisposition' => ContentDisposition::class,
|
||||
'content_disposition' => ContentDisposition::class,
|
||||
'content-disposition' => ContentDisposition::class,
|
||||
'contenttype' => ContentType::class,
|
||||
'content_type' => ContentType::class,
|
||||
'content-type' => ContentType::class,
|
||||
'contenttransferencoding' => ContentTransferEncoding::class,
|
||||
'content_transfer_encoding' => ContentTransferEncoding::class,
|
||||
'content-transfer-encoding' => ContentTransferEncoding::class,
|
||||
'date' => Date::class,
|
||||
'from' => From::class,
|
||||
'in-reply-to' => InReplyTo::class,
|
||||
'message-id' => MessageId::class,
|
||||
'mimeversion' => MimeVersion::class,
|
||||
'mime_version' => MimeVersion::class,
|
||||
'mime-version' => MimeVersion::class,
|
||||
'received' => Received::class,
|
||||
'references' => References::class,
|
||||
'replyto' => ReplyTo::class,
|
||||
'reply_to' => ReplyTo::class,
|
||||
'reply-to' => ReplyTo::class,
|
||||
'sender' => Sender::class,
|
||||
'subject' => Subject::class,
|
||||
'to' => To::class,
|
||||
];
|
||||
|
||||
public function get(string $name, ?string $default = null): ?string
|
||||
{
|
||||
$name = $this->normalizeName($name);
|
||||
return isset($this->plugins[$name]) ? $this->plugins[$name] : $default;
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->plugins[$this->normalizeName($name)]);
|
||||
}
|
||||
|
||||
public function add(string $name, string $class): void
|
||||
{
|
||||
$this->plugins[$this->normalizeName($name)] = $class;
|
||||
}
|
||||
|
||||
public function remove(string $name): void
|
||||
{
|
||||
unset($this->plugins[$this->normalizeName($name)]);
|
||||
}
|
||||
|
||||
private function normalizeName(string $name): string
|
||||
{
|
||||
return strtolower($name);
|
||||
}
|
||||
}
|
||||
25
vendor/laminas/laminas-mail/src/Header/HeaderLocatorInterface.php
vendored
Normal file
25
vendor/laminas/laminas-mail/src/Header/HeaderLocatorInterface.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
/**
|
||||
* Interface detailing how to resolve header names to classes.
|
||||
*/
|
||||
interface HeaderLocatorInterface
|
||||
{
|
||||
public function get(string $name, ?string $default = null): ?string;
|
||||
|
||||
public function has(string $name): bool;
|
||||
|
||||
public function add(string $name, string $class): void;
|
||||
|
||||
public function remove(string $name): void;
|
||||
}
|
||||
73
vendor/laminas/laminas-mail/src/Header/HeaderName.php
vendored
Normal file
73
vendor/laminas/laminas-mail/src/Header/HeaderName.php
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
final class HeaderName
|
||||
{
|
||||
/**
|
||||
* No public constructor.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the header name according to RFC 2822
|
||||
*
|
||||
* @see http://www.rfc-base.org/txt/rfc-2822.txt (section 2.2)
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public static function filter($name)
|
||||
{
|
||||
$result = '';
|
||||
$tot = strlen($name);
|
||||
for ($i = 0; $i < $tot; $i += 1) {
|
||||
$ord = ord($name[$i]);
|
||||
if ($ord > 32 && $ord < 127 && $ord !== 58) {
|
||||
$result .= $name[$i];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the header name contains any invalid characters.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($name)
|
||||
{
|
||||
$tot = strlen($name);
|
||||
for ($i = 0; $i < $tot; $i += 1) {
|
||||
$ord = ord($name[$i]);
|
||||
if ($ord < 33 || $ord > 126 || $ord === 58) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the header name is valid.
|
||||
*
|
||||
* Raises an exception if invalid.
|
||||
*
|
||||
* @param string $name
|
||||
* @throws Exception\RuntimeException
|
||||
* @return void
|
||||
*/
|
||||
public static function assertValid($name)
|
||||
{
|
||||
if (! self::isValid($name)) {
|
||||
throw new Exception\RuntimeException('Invalid header name detected');
|
||||
}
|
||||
}
|
||||
}
|
||||
116
vendor/laminas/laminas-mail/src/Header/HeaderValue.php
vendored
Normal file
116
vendor/laminas/laminas-mail/src/Header/HeaderValue.php
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
final class HeaderValue
|
||||
{
|
||||
/**
|
||||
* No public constructor.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the header value according to RFC 2822
|
||||
*
|
||||
* @see http://www.rfc-base.org/txt/rfc-2822.txt (section 2.2)
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function filter($value)
|
||||
{
|
||||
$result = '';
|
||||
$total = strlen($value);
|
||||
|
||||
// Filter for CR and LF characters, leaving CRLF + WSP sequences for
|
||||
// Long Header Fields (section 2.2.3 of RFC 2822)
|
||||
for ($i = 0; $i < $total; $i += 1) {
|
||||
$ord = ord($value[$i]);
|
||||
if ($ord === 10 || $ord > 127) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ord === 13) {
|
||||
if ($i + 2 >= $total) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lf = ord($value[$i + 1]);
|
||||
$sp = ord($value[$i + 2]);
|
||||
|
||||
if ($lf !== 10 || $sp !== 32) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result .= "\r\n ";
|
||||
$i += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
$result .= $value[$i];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the header value contains any invalid characters.
|
||||
*
|
||||
* @see http://www.rfc-base.org/txt/rfc-2822.txt (section 2.2)
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($value)
|
||||
{
|
||||
$total = strlen($value);
|
||||
for ($i = 0; $i < $total; $i += 1) {
|
||||
$ord = ord($value[$i]);
|
||||
|
||||
// bare LF means we aren't valid
|
||||
if ($ord === 10 || $ord > 127) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($ord === 13) {
|
||||
if ($i + 2 >= $total) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$lf = ord($value[$i + 1]);
|
||||
$sp = ord($value[$i + 2]);
|
||||
|
||||
if ($lf !== 10 || ! in_array($sp, [9, 32], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip over the LF following this
|
||||
$i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the header value is valid.
|
||||
*
|
||||
* Raises an exception if invalid.
|
||||
*
|
||||
* @param string $value
|
||||
* @throws Exception\RuntimeException
|
||||
* @return void
|
||||
*/
|
||||
public static function assertValid($value)
|
||||
{
|
||||
if (! self::isValid($value)) {
|
||||
throw new Exception\RuntimeException('Invalid header value detected');
|
||||
}
|
||||
}
|
||||
}
|
||||
161
vendor/laminas/laminas-mail/src/Header/HeaderWrap.php
vendored
Normal file
161
vendor/laminas/laminas-mail/src/Header/HeaderWrap.php
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mail\Headers;
|
||||
use Laminas\Mime\Mime;
|
||||
|
||||
/**
|
||||
* Utility class used for creating wrapped or MIME-encoded versions of header
|
||||
* values.
|
||||
*/
|
||||
abstract class HeaderWrap
|
||||
{
|
||||
/**
|
||||
* Wrap a long header line
|
||||
*
|
||||
* @param string $value
|
||||
* @param HeaderInterface $header
|
||||
* @return string
|
||||
*/
|
||||
public static function wrap($value, HeaderInterface $header)
|
||||
{
|
||||
if ($header instanceof UnstructuredInterface) {
|
||||
return static::wrapUnstructuredHeader($value, $header);
|
||||
} elseif ($header instanceof StructuredInterface) {
|
||||
return static::wrapStructuredHeader($value, $header);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an unstructured header line
|
||||
*
|
||||
* Wrap at 78 characters or before, based on whitespace.
|
||||
*
|
||||
* @param string $value
|
||||
* @param HeaderInterface $header
|
||||
* @return string
|
||||
*/
|
||||
protected static function wrapUnstructuredHeader($value, HeaderInterface $header)
|
||||
{
|
||||
$encoding = $header->getEncoding();
|
||||
if ($encoding == 'ASCII') {
|
||||
return wordwrap($value, 78, Headers::FOLDING);
|
||||
}
|
||||
return static::mimeEncodeValue($value, $encoding, 78);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a structured header line
|
||||
*
|
||||
* @param string $value
|
||||
* @param StructuredInterface $header
|
||||
* @return string
|
||||
*/
|
||||
protected static function wrapStructuredHeader($value, StructuredInterface $header)
|
||||
{
|
||||
$delimiter = $header->getDelimiter();
|
||||
|
||||
$length = strlen($value);
|
||||
$lines = [];
|
||||
$temp = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$temp .= $value[$i];
|
||||
if ($value[$i] == $delimiter) {
|
||||
$lines[] = $temp;
|
||||
$temp = '';
|
||||
}
|
||||
}
|
||||
return implode(Headers::FOLDING, $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* MIME-encode a value
|
||||
*
|
||||
* Performs quoted-printable encoding on a value, setting maximum
|
||||
* line-length to 998.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $encoding
|
||||
* @param int $lineLength maximum line-length, by default 998
|
||||
* @return string Returns the mime encode value without the last line ending
|
||||
*/
|
||||
public static function mimeEncodeValue($value, $encoding, $lineLength = 998)
|
||||
{
|
||||
return Mime::encodeQuotedPrintableHeader($value, $encoding, $lineLength, Headers::EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* MIME-decode a value
|
||||
*
|
||||
* Performs quoted-printable decoding on a value.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string Returns the mime encode value without the last line ending
|
||||
*/
|
||||
public static function mimeDecodeValue($value)
|
||||
{
|
||||
// unfold first, because iconv_mime_decode is discarding "\n" with no apparent reason
|
||||
// making the resulting value no longer valid.
|
||||
|
||||
// see https://tools.ietf.org/html/rfc2822#section-2.2.3 about unfolding
|
||||
$parts = explode(Headers::FOLDING, $value);
|
||||
$value = implode(' ', $parts);
|
||||
|
||||
$decodedValue = iconv_mime_decode($value, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
|
||||
|
||||
// imap (unlike iconv) can handle multibyte headers which are splitted across multiple line
|
||||
if (self::isNotDecoded($value, $decodedValue) && extension_loaded('imap')) {
|
||||
return array_reduce(
|
||||
imap_mime_header_decode(imap_utf8($value)),
|
||||
function ($accumulator, $headerPart) {
|
||||
return $accumulator . $headerPart->text;
|
||||
},
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
return $decodedValue;
|
||||
}
|
||||
|
||||
private static function isNotDecoded($originalValue, $value)
|
||||
{
|
||||
return 0 === strpos($value, '=?')
|
||||
&& strlen($value) - 2 === strpos($value, '?=')
|
||||
&& false !== strpos($originalValue, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if is possible apply MIME-encoding
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public static function canBeEncoded($value)
|
||||
{
|
||||
// avoid any wrapping by specifying line length long enough
|
||||
// "test" -> 4
|
||||
// "x-test: =?ISO-8859-1?B?dGVzdA==?=" -> 33
|
||||
// 8 +2 +3 +3 -> 16
|
||||
$charset = 'UTF-8';
|
||||
$lineLength = strlen($value) * 4 + strlen($charset) + 16;
|
||||
|
||||
$preferences = [
|
||||
'scheme' => 'Q',
|
||||
'input-charset' => $charset,
|
||||
'output-charset' => $charset,
|
||||
'line-length' => $lineLength,
|
||||
];
|
||||
|
||||
$encoded = iconv_mime_encode('x-test', $value, $preferences);
|
||||
|
||||
return (false !== $encoded);
|
||||
}
|
||||
}
|
||||
143
vendor/laminas/laminas-mail/src/Header/IdentificationField.php
vendored
Normal file
143
vendor/laminas/laminas-mail/src/Header/IdentificationField.php
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mail\Headers;
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5322#section-3.6.4
|
||||
*/
|
||||
abstract class IdentificationField implements HeaderInterface
|
||||
{
|
||||
/**
|
||||
* @var string lower case field name
|
||||
*/
|
||||
protected static $type;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $messageIds;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* @param string $headerLine
|
||||
* @return static
|
||||
*/
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
if (strtolower($name) !== static::$type) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'Invalid header line for "%s" string',
|
||||
__CLASS__
|
||||
));
|
||||
}
|
||||
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
$messageIds = array_map(
|
||||
[IdentificationField::class, "trimMessageId"],
|
||||
explode(" ", $value)
|
||||
);
|
||||
|
||||
$header = new static();
|
||||
$header->setIds($messageIds);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
private static function trimMessageId($id)
|
||||
{
|
||||
return trim($id, "\t\n\r\0\x0B<>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFieldName()
|
||||
{
|
||||
return $this->fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $format
|
||||
* @return string
|
||||
*/
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
return implode(Headers::FOLDING, array_map(function ($id) {
|
||||
return sprintf('<%s>', $id);
|
||||
}, $this->messageIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $encoding Ignored; headers of this type MUST always be in
|
||||
* ASCII.
|
||||
* @return static This method is a no-op, and implements a fluent interface.
|
||||
*/
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Always returns ASCII
|
||||
*/
|
||||
public function getEncoding()
|
||||
{
|
||||
return 'ASCII';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message ids
|
||||
*
|
||||
* @param string[] $ids
|
||||
* @return static This method implements a fluent interface.
|
||||
*/
|
||||
public function setIds($ids)
|
||||
{
|
||||
foreach ($ids as $id) {
|
||||
if (! HeaderValue::isValid($id)
|
||||
|| preg_match("/[\r\n]/", $id)
|
||||
) {
|
||||
throw new Exception\InvalidArgumentException('Invalid ID detected');
|
||||
}
|
||||
}
|
||||
|
||||
$this->messageIds = array_map([IdentificationField::class, "trimMessageId"], $ids);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the message ids
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getIds()
|
||||
{
|
||||
return $this->messageIds;
|
||||
}
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/InReplyTo.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/InReplyTo.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class InReplyTo extends IdentificationField
|
||||
{
|
||||
protected $fieldName = 'In-Reply-To';
|
||||
protected static $type = 'in-reply-to';
|
||||
}
|
||||
99
vendor/laminas/laminas-mail/src/Header/ListParser.php
vendored
Normal file
99
vendor/laminas/laminas-mail/src/Header/ListParser.php
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ListParser
|
||||
{
|
||||
const CHAR_QUOTES = ['\'', '"'];
|
||||
const CHAR_DELIMS = [',', ';'];
|
||||
const CHAR_ESCAPE = '\\';
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param array $delims Delimiters allowed between values; parser will
|
||||
* split on these, as long as they are not within quotes. Defaults
|
||||
* to ListParser::CHAR_DELIMS.
|
||||
* @return array
|
||||
*/
|
||||
public static function parse($value, array $delims = self::CHAR_DELIMS)
|
||||
{
|
||||
$values = [];
|
||||
$length = strlen($value);
|
||||
$currentValue = '';
|
||||
$inEscape = false;
|
||||
$inQuote = false;
|
||||
$currentQuoteDelim = null;
|
||||
|
||||
for ($i = 0; $i < $length; $i += 1) {
|
||||
$char = $value[$i];
|
||||
|
||||
// If we are in an escape sequence, append the character and continue.
|
||||
if ($inEscape) {
|
||||
$currentValue .= $char;
|
||||
$inEscape = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are not in a quoted string, and have a delimiter, append
|
||||
// the current value to the list, and reset the current value.
|
||||
if (in_array($char, $delims, true) && ! $inQuote) {
|
||||
$values [] = $currentValue;
|
||||
$currentValue = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Append the character to the current value
|
||||
$currentValue .= $char;
|
||||
|
||||
// Escape sequence discovered.
|
||||
if (self::CHAR_ESCAPE === $char) {
|
||||
$inEscape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the character is not a quote character, we are done
|
||||
// processing it.
|
||||
if (! in_array($char, self::CHAR_QUOTES)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the character matches a previously matched quote delimiter,
|
||||
// we reset our quote status and the currently opened quote
|
||||
// delimiter.
|
||||
if ($char === $currentQuoteDelim) {
|
||||
$inQuote = false;
|
||||
$currentQuoteDelim = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If already in quote and the character does not match the previously
|
||||
// matched quote delimiter, we're done here.
|
||||
if ($inQuote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, we're starting a quoted string.
|
||||
$inQuote = true;
|
||||
$currentQuoteDelim = $char;
|
||||
}
|
||||
|
||||
// If we reached the end of the string and still have a current value,
|
||||
// append it to the list (no delimiter was reached).
|
||||
if ('' !== $currentValue) {
|
||||
$values [] = $currentValue;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
119
vendor/laminas/laminas-mail/src/Header/MessageId.php
vendored
Normal file
119
vendor/laminas/laminas-mail/src/Header/MessageId.php
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class MessageId implements HeaderInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $messageId;
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'message-id') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for Message-ID string');
|
||||
}
|
||||
|
||||
$header = new static();
|
||||
$header->setId($value);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Message-ID';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
// This header must be always in US-ASCII
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
return 'ASCII';
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'Message-ID: ' . $this->getFieldValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message id
|
||||
*
|
||||
* @param string|null $id
|
||||
* @return MessageId
|
||||
*/
|
||||
public function setId($id = null)
|
||||
{
|
||||
if ($id === null) {
|
||||
$id = $this->createMessageId();
|
||||
} else {
|
||||
$id = trim($id, '<>');
|
||||
}
|
||||
|
||||
if (! HeaderValue::isValid($id)
|
||||
|| preg_match("/[\r\n]/", $id)
|
||||
) {
|
||||
throw new Exception\InvalidArgumentException('Invalid ID detected');
|
||||
}
|
||||
|
||||
$this->messageId = sprintf('<%s>', $id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the message id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Message-ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function createMessageId()
|
||||
{
|
||||
$time = time();
|
||||
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
$user = $_SERVER['REMOTE_ADDR'];
|
||||
} else {
|
||||
$user = getmypid();
|
||||
}
|
||||
|
||||
$rand = mt_rand();
|
||||
|
||||
if (isset($_SERVER["SERVER_NAME"])) {
|
||||
$hostName = $_SERVER["SERVER_NAME"];
|
||||
} else {
|
||||
$hostName = php_uname('n');
|
||||
}
|
||||
|
||||
return sha1($time . $user . $rand) . '@' . $hostName;
|
||||
}
|
||||
}
|
||||
87
vendor/laminas/laminas-mail/src/Header/MimeVersion.php
vendored
Normal file
87
vendor/laminas/laminas-mail/src/Header/MimeVersion.php
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class MimeVersion implements HeaderInterface
|
||||
{
|
||||
/**
|
||||
* @var string Version string
|
||||
*/
|
||||
protected $version = '1.0';
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'mime-version') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for MIME-Version string');
|
||||
}
|
||||
|
||||
// Check for version, and set if found
|
||||
$header = new static();
|
||||
if (preg_match('/^(?P<version>\d+\.\d+)$/', $value, $matches)) {
|
||||
$header->setVersion($matches['version']);
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'MIME-Version';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
// This header must be always in US-ASCII
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
return 'ASCII';
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'MIME-Version: ' . $this->getFieldValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version string used in this header
|
||||
*
|
||||
* @param string $version
|
||||
* @return MimeVersion
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
if (! preg_match('/^[1-9]\d*\.\d+$/', $version)) {
|
||||
throw new Exception\InvalidArgumentException('Invalid MIME-Version value detected');
|
||||
}
|
||||
$this->version = $version;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the version string for this header
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
}
|
||||
14
vendor/laminas/laminas-mail/src/Header/MultipleHeadersInterface.php
vendored
Normal file
14
vendor/laminas/laminas-mail/src/Header/MultipleHeadersInterface.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
interface MultipleHeadersInterface extends HeaderInterface
|
||||
{
|
||||
public function toStringMultipleHeaders(array $headers);
|
||||
}
|
||||
92
vendor/laminas/laminas-mail/src/Header/Received.php
vendored
Normal file
92
vendor/laminas/laminas-mail/src/Header/Received.php
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mail\Headers;
|
||||
|
||||
/**
|
||||
* @todo Allow setting date from DateTime, Laminas\Date, or string
|
||||
*/
|
||||
class Received implements HeaderInterface, MultipleHeadersInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'received') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for Received string');
|
||||
}
|
||||
|
||||
$header = new static($value);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function __construct($value = '')
|
||||
{
|
||||
if (! HeaderValue::isValid($value)) {
|
||||
throw new Exception\InvalidArgumentException('Invalid Received value provided');
|
||||
}
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Received';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
// This header must be always in US-ASCII
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
return 'ASCII';
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'Received: ' . $this->getFieldValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize collection of Received headers to string
|
||||
*
|
||||
* @param array $headers
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
public function toStringMultipleHeaders(array $headers)
|
||||
{
|
||||
$strings = [$this->toString()];
|
||||
foreach ($headers as $header) {
|
||||
if (! $header instanceof Received) {
|
||||
throw new Exception\RuntimeException(
|
||||
'The Received multiple header implementation can only accept an array of Received headers'
|
||||
);
|
||||
}
|
||||
$strings[] = $header->toString();
|
||||
}
|
||||
return implode(Headers::EOL, $strings);
|
||||
}
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/References.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/References.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class References extends IdentificationField
|
||||
{
|
||||
protected $fieldName = 'References';
|
||||
protected static $type = 'references';
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/ReplyTo.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/ReplyTo.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class ReplyTo extends AbstractAddressList
|
||||
{
|
||||
protected $fieldName = 'Reply-To';
|
||||
protected static $type = 'reply-to';
|
||||
}
|
||||
153
vendor/laminas/laminas-mail/src/Header/Sender.php
vendored
Normal file
153
vendor/laminas/laminas-mail/src/Header/Sender.php
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mail;
|
||||
use Laminas\Mime\Mime;
|
||||
|
||||
/**
|
||||
* Sender header class methods.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2822 RFC 2822
|
||||
* @see https://tools.ietf.org/html/rfc2047 RFC 2047
|
||||
*/
|
||||
class Sender implements HeaderInterface
|
||||
{
|
||||
/**
|
||||
* @var \Laminas\Mail\Address\AddressInterface
|
||||
*/
|
||||
protected $address;
|
||||
|
||||
/**
|
||||
* Header encoding
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $encoding;
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'sender') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header name for Sender string');
|
||||
}
|
||||
|
||||
$header = new static();
|
||||
|
||||
/**
|
||||
* matches the header value so that the email must be enclosed by < > when a name is present
|
||||
* 'name' and 'email' capture groups correspond respectively to 'display-name' and 'addr-spec' in the ABNF
|
||||
* @see https://tools.ietf.org/html/rfc5322#section-3.4
|
||||
*/
|
||||
$hasMatches = preg_match(
|
||||
'/^(?:(?P<name>.+)\s)?(?(name)<|<?)(?P<email>[^\s]+?)(?(name)>|>?)$/',
|
||||
$value,
|
||||
$matches
|
||||
);
|
||||
|
||||
if ($hasMatches !== 1) {
|
||||
throw new Exception\InvalidArgumentException('Invalid header value for Sender string');
|
||||
}
|
||||
|
||||
$senderName = trim($matches['name']);
|
||||
|
||||
if (empty($senderName)) {
|
||||
$senderName = null;
|
||||
}
|
||||
|
||||
$header->setAddress($matches['email'], $senderName);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Sender';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
if (! $this->address instanceof Mail\Address\AddressInterface) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$email = sprintf('<%s>', $this->address->getEmail());
|
||||
$name = $this->address->getName();
|
||||
|
||||
if (! empty($name)) {
|
||||
if ($format == HeaderInterface::FORMAT_ENCODED) {
|
||||
$encoding = $this->getEncoding();
|
||||
if ('ASCII' !== $encoding) {
|
||||
$name = HeaderWrap::mimeEncodeValue($name, $encoding);
|
||||
}
|
||||
}
|
||||
$email = sprintf('%s %s', $name, $email);
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
if (! $this->encoding) {
|
||||
$this->encoding = Mime::isPrintable($this->getFieldValue(HeaderInterface::FORMAT_RAW))
|
||||
? 'ASCII'
|
||||
: 'UTF-8';
|
||||
}
|
||||
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'Sender: ' . $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address used in this header
|
||||
*
|
||||
* @param string|\Laminas\Mail\Address\AddressInterface $emailOrAddress
|
||||
* @param null|string $name
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return Sender
|
||||
*/
|
||||
public function setAddress($emailOrAddress, $name = null)
|
||||
{
|
||||
if (is_string($emailOrAddress)) {
|
||||
$emailOrAddress = new Mail\Address($emailOrAddress, $name);
|
||||
} elseif (! $emailOrAddress instanceof Mail\Address\AddressInterface) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a string or AddressInterface object; received "%s"',
|
||||
__METHOD__,
|
||||
(is_object($emailOrAddress) ? get_class($emailOrAddress) : gettype($emailOrAddress))
|
||||
));
|
||||
}
|
||||
$this->address = $emailOrAddress;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the internal address from this header
|
||||
*
|
||||
* @return \Laminas\Mail\Address\AddressInterface|null
|
||||
*/
|
||||
public function getAddress()
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
}
|
||||
19
vendor/laminas/laminas-mail/src/Header/StructuredInterface.php
vendored
Normal file
19
vendor/laminas/laminas-mail/src/Header/StructuredInterface.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
interface StructuredInterface extends HeaderInterface
|
||||
{
|
||||
/**
|
||||
* Return the delimiter at which a header line should be wrapped
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiter();
|
||||
}
|
||||
119
vendor/laminas/laminas-mail/src/Header/Subject.php
vendored
Normal file
119
vendor/laminas/laminas-mail/src/Header/Subject.php
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
use Laminas\Mime\Mime;
|
||||
|
||||
/**
|
||||
* Subject header class methods.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2822 RFC 2822
|
||||
* @see https://tools.ietf.org/html/rfc2047 RFC 2047
|
||||
*/
|
||||
class Subject implements UnstructuredInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $subject = '';
|
||||
|
||||
/**
|
||||
* Header encoding
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $encoding;
|
||||
|
||||
public static function fromString($headerLine)
|
||||
{
|
||||
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
||||
$value = HeaderWrap::mimeDecodeValue($value);
|
||||
|
||||
// check to ensure proper header type for this factory
|
||||
if (strtolower($name) !== 'subject') {
|
||||
throw new Exception\InvalidArgumentException('Invalid header line for Subject string');
|
||||
}
|
||||
|
||||
$header = new static();
|
||||
$header->setSubject($value);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
public function getFieldName()
|
||||
{
|
||||
return 'Subject';
|
||||
}
|
||||
|
||||
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
if (HeaderInterface::FORMAT_ENCODED === $format) {
|
||||
return HeaderWrap::wrap($this->subject, $this);
|
||||
}
|
||||
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
if ($encoding === $this->encoding) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($encoding === null) {
|
||||
$this->encoding = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$encoding = strtoupper($encoding);
|
||||
if ($encoding === 'UTF-8') {
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($encoding === 'ASCII' && Mime::isPrintable($this->subject)) {
|
||||
$this->encoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->encoding = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncoding()
|
||||
{
|
||||
if (! $this->encoding) {
|
||||
$this->encoding = Mime::isPrintable($this->subject) ? 'ASCII' : 'UTF-8';
|
||||
}
|
||||
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
public function setSubject($subject)
|
||||
{
|
||||
$subject = (string) $subject;
|
||||
|
||||
if (! HeaderWrap::canBeEncoded($subject)) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
'Subject value must be composed of printable US-ASCII or UTF-8 characters.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->subject = $subject;
|
||||
$this->encoding = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'Subject: ' . $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
}
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Header/To.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Header/To.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
class To extends AbstractAddressList
|
||||
{
|
||||
protected $fieldName = 'To';
|
||||
protected static $type = 'to';
|
||||
}
|
||||
16
vendor/laminas/laminas-mail/src/Header/UnstructuredInterface.php
vendored
Normal file
16
vendor/laminas/laminas-mail/src/Header/UnstructuredInterface.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Header;
|
||||
|
||||
/**
|
||||
* Marker interface for unstructured headers.
|
||||
*/
|
||||
interface UnstructuredInterface extends HeaderInterface
|
||||
{
|
||||
}
|
||||
606
vendor/laminas/laminas-mail/src/Headers.php
vendored
Normal file
606
vendor/laminas/laminas-mail/src/Headers.php
vendored
Normal file
@ -0,0 +1,606 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use Iterator;
|
||||
use Laminas\Loader\PluginClassLoader;
|
||||
use Laminas\Loader\PluginClassLocator;
|
||||
use Laminas\Mail\Header\GenericHeader;
|
||||
use Laminas\Mail\Header\HeaderInterface;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Basic mail headers collection functionality
|
||||
*
|
||||
* Handles aggregation of headers
|
||||
*/
|
||||
class Headers implements Countable, Iterator
|
||||
{
|
||||
/** @var string End of Line for fields */
|
||||
const EOL = "\r\n";
|
||||
|
||||
/** @var string Start of Line when folding */
|
||||
const FOLDING = "\r\n ";
|
||||
|
||||
/**
|
||||
* @var null|Header\HeaderLocatorInterface
|
||||
*/
|
||||
private $headerLocator;
|
||||
|
||||
/**
|
||||
* @todo Remove for 3.0.0.
|
||||
* @var null|PluginClassLocator
|
||||
*/
|
||||
protected $pluginClassLoader;
|
||||
|
||||
/**
|
||||
* @var array key names for $headers array
|
||||
*/
|
||||
protected $headersKeys = [];
|
||||
|
||||
/**
|
||||
* @var Header\HeaderInterface[] instances
|
||||
*/
|
||||
protected $headers = [];
|
||||
|
||||
/**
|
||||
* Header encoding; defaults to ASCII
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $encoding = 'ASCII';
|
||||
|
||||
/**
|
||||
* Populates headers from string representation
|
||||
*
|
||||
* Parses a string for headers, and aggregates them, in order, in the
|
||||
* current instance, primarily as strings until they are needed (they
|
||||
* will be lazy loaded)
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $EOL EOL string; defaults to {@link EOL}
|
||||
* @throws Exception\RuntimeException
|
||||
* @return Headers
|
||||
*/
|
||||
public static function fromString($string, $EOL = self::EOL)
|
||||
{
|
||||
$headers = new static();
|
||||
$currentLine = '';
|
||||
$emptyLine = 0;
|
||||
|
||||
// iterate the header lines, some might be continuations
|
||||
$lines = explode($EOL, $string);
|
||||
$total = count($lines);
|
||||
for ($i = 0; $i < $total; $i += 1) {
|
||||
$line = $lines[$i];
|
||||
|
||||
// Empty line indicates end of headers
|
||||
// EXCEPT if there are more lines, in which case, there's a possible error condition
|
||||
if (preg_match('/^\s*$/', $line)) {
|
||||
$emptyLine += 1;
|
||||
if ($emptyLine > 2) {
|
||||
throw new Exception\RuntimeException('Malformed header detected');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($emptyLine > 1) {
|
||||
throw new Exception\RuntimeException('Malformed header detected');
|
||||
}
|
||||
|
||||
// check if a header name is present
|
||||
if (preg_match('/^[\x21-\x39\x3B-\x7E]+:.*$/', $line)) {
|
||||
if ($currentLine) {
|
||||
// a header name was present, then store the current complete line
|
||||
$headers->addHeaderLine($currentLine);
|
||||
}
|
||||
$currentLine = trim($line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// continuation: append to current line
|
||||
// recover the whitespace that break the line (unfolding, rfc2822#section-2.2.3)
|
||||
if (preg_match('/^\s+.*$/', $line)) {
|
||||
$currentLine .= ' ' . trim($line);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Line does not match header format!
|
||||
throw new Exception\RuntimeException(sprintf(
|
||||
'Line "%s" does not match header format!',
|
||||
$line
|
||||
));
|
||||
}
|
||||
if ($currentLine) {
|
||||
$headers->addHeaderLine($currentLine);
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an alternate PluginClassLocator implementation for loading header classes.
|
||||
*
|
||||
* @deprecated since 2.12.0
|
||||
* @todo Remove for version 3.0.0
|
||||
* @return $this
|
||||
*/
|
||||
public function setPluginClassLoader(PluginClassLocator $pluginClassLoader)
|
||||
{
|
||||
// Silenced; can be caught in custom error handlers.
|
||||
@trigger_error(sprintf(
|
||||
'Since laminas/laminas-mail 2.12.0: Usage of %s is deprecated; use %s::setHeaderLocator() instead',
|
||||
__METHOD__,
|
||||
__CLASS__
|
||||
), E_USER_DEPRECATED);
|
||||
|
||||
$this->pluginClassLoader = $pluginClassLoader;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PluginClassLocator instance for customizing headers.
|
||||
*
|
||||
* Lazyloads a Header\HeaderLoader if necessary.
|
||||
*
|
||||
* @deprecated since 2.12.0
|
||||
* @todo Remove for version 3.0.0
|
||||
* @return PluginClassLocator
|
||||
*/
|
||||
public function getPluginClassLoader()
|
||||
{
|
||||
// Silenced; can be caught in custom error handlers.
|
||||
@trigger_error(sprintf(
|
||||
'Since laminas/laminas-mail 2.12.0: Usage of %s is deprecated; use %s::getHeaderLocator() instead',
|
||||
__METHOD__,
|
||||
__CLASS__
|
||||
), E_USER_DEPRECATED);
|
||||
|
||||
if (! $this->pluginClassLoader) {
|
||||
$this->pluginClassLoader = new Header\HeaderLoader();
|
||||
}
|
||||
|
||||
return $this->pluginClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the header class locator for customizing headers.
|
||||
*
|
||||
* Lazyloads a Header\HeaderLocator instance if necessary.
|
||||
*/
|
||||
public function getHeaderLocator(): Header\HeaderLocatorInterface
|
||||
{
|
||||
if (! $this->headerLocator) {
|
||||
$this->setHeaderLocator(new Header\HeaderLocator());
|
||||
}
|
||||
return $this->headerLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Return self when we update to 7.4 or later as minimum PHP version.
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeaderLocator(Header\HeaderLocatorInterface $headerLocator)
|
||||
{
|
||||
$this->headerLocator = $headerLocator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the header encoding
|
||||
*
|
||||
* @param string $encoding
|
||||
* @return Headers
|
||||
*/
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->encoding = $encoding;
|
||||
foreach ($this as $header) {
|
||||
$header->setEncoding($encoding);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header encoding
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncoding()
|
||||
{
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add many headers at once
|
||||
*
|
||||
* Expects an array (or Traversable object) of type/value pairs.
|
||||
*
|
||||
* @param array|Traversable $headers
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return Headers
|
||||
*/
|
||||
public function addHeaders($headers)
|
||||
{
|
||||
if (! is_array($headers) && ! $headers instanceof Traversable) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'Expected array or Traversable; received "%s"',
|
||||
(is_object($headers) ? get_class($headers) : gettype($headers))
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
if (is_int($name)) {
|
||||
if (is_string($value)) {
|
||||
$this->addHeaderLine($value);
|
||||
} elseif (is_array($value) && count($value) == 1) {
|
||||
$this->addHeaderLine(key($value), current($value));
|
||||
} elseif (is_array($value) && count($value) == 2) {
|
||||
$this->addHeaderLine($value[0], $value[1]);
|
||||
} elseif ($value instanceof Header\HeaderInterface) {
|
||||
$this->addHeader($value);
|
||||
}
|
||||
} elseif (is_string($name)) {
|
||||
$this->addHeaderLine($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a raw header line, either in name => value, or as a single string 'name: value'
|
||||
*
|
||||
* This method allows for lazy-loading in that the parsing and instantiation of HeaderInterface object
|
||||
* will be delayed until they are retrieved by either get() or current()
|
||||
*
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @param string $headerFieldNameOrLine
|
||||
* @param string $fieldValue optional
|
||||
* @return Headers
|
||||
*/
|
||||
public function addHeaderLine($headerFieldNameOrLine, $fieldValue = null)
|
||||
{
|
||||
if (! is_string($headerFieldNameOrLine)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects its first argument to be a string; received "%s"',
|
||||
__METHOD__,
|
||||
(is_object($headerFieldNameOrLine)
|
||||
? get_class($headerFieldNameOrLine)
|
||||
: gettype($headerFieldNameOrLine))
|
||||
));
|
||||
}
|
||||
|
||||
if ($fieldValue === null) {
|
||||
$headers = $this->loadHeader($headerFieldNameOrLine);
|
||||
$headers = is_array($headers) ? $headers : [$headers];
|
||||
foreach ($headers as $header) {
|
||||
$this->addHeader($header);
|
||||
}
|
||||
} elseif (is_array($fieldValue)) {
|
||||
foreach ($fieldValue as $i) {
|
||||
$this->addHeader(Header\GenericMultiHeader::fromString($headerFieldNameOrLine . ':' . $i));
|
||||
}
|
||||
} else {
|
||||
$this->addHeader(Header\GenericHeader::fromString($headerFieldNameOrLine . ':' . $fieldValue));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Header\Interface to this container, for raw values see {@link addHeaderLine()} and {@link addHeaders()}
|
||||
*
|
||||
* @param Header\HeaderInterface $header
|
||||
* @return Headers
|
||||
*/
|
||||
public function addHeader(Header\HeaderInterface $header)
|
||||
{
|
||||
$key = $this->normalizeFieldName($header->getFieldName());
|
||||
$this->headersKeys[] = $key;
|
||||
$this->headers[] = $header;
|
||||
if ($this->getEncoding() !== 'ASCII') {
|
||||
$header->setEncoding($this->getEncoding());
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Header from the container
|
||||
*
|
||||
* @param string|Header\HeaderInterface field name or specific header instance to remove
|
||||
* @return bool
|
||||
*/
|
||||
public function removeHeader($instanceOrFieldName)
|
||||
{
|
||||
if (! $instanceOrFieldName instanceof Header\HeaderInterface && ! is_string($instanceOrFieldName)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s requires a string or %s instance; received %s',
|
||||
__METHOD__,
|
||||
Header\HeaderInterface::class,
|
||||
is_object($instanceOrFieldName) ? get_class($instanceOrFieldName) : gettype($instanceOrFieldName)
|
||||
));
|
||||
}
|
||||
|
||||
if ($instanceOrFieldName instanceof Header\HeaderInterface) {
|
||||
$indexes = array_keys($this->headers, $instanceOrFieldName, true);
|
||||
}
|
||||
|
||||
if (is_string($instanceOrFieldName)) {
|
||||
$key = $this->normalizeFieldName($instanceOrFieldName);
|
||||
$indexes = array_keys($this->headersKeys, $key, true);
|
||||
}
|
||||
|
||||
if (! empty($indexes)) {
|
||||
foreach ($indexes as $index) {
|
||||
unset($this->headersKeys[$index]);
|
||||
unset($this->headers[$index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all headers
|
||||
*
|
||||
* Removes all headers from queue
|
||||
*
|
||||
* @return Headers
|
||||
*/
|
||||
public function clearHeaders()
|
||||
{
|
||||
$this->headers = $this->headersKeys = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers of a certain name/type
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool|ArrayIterator|Header\HeaderInterface Returns false if there is no headers with $name in this
|
||||
* contain, an ArrayIterator if the header is a MultipleHeadersInterface instance and finally returns
|
||||
* HeaderInterface for the rest of cases.
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
$key = $this->normalizeFieldName($name);
|
||||
$results = [];
|
||||
|
||||
foreach (array_keys($this->headersKeys, $key) as $index) {
|
||||
if ($this->headers[$index] instanceof Header\GenericHeader) {
|
||||
$results[] = $this->lazyLoadHeader($index);
|
||||
} else {
|
||||
$results[] = $this->headers[$index];
|
||||
}
|
||||
}
|
||||
|
||||
switch (count($results)) {
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
if ($results[0] instanceof Header\MultipleHeadersInterface) {
|
||||
return new ArrayIterator($results);
|
||||
} else {
|
||||
return $results[0];
|
||||
}
|
||||
//fall-trough
|
||||
default:
|
||||
return new ArrayIterator($results);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for existence of a type of header
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$name = $this->normalizeFieldName($name);
|
||||
return in_array($name, $this->headersKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the pointer for this object as an iterator
|
||||
*
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
next($this->headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current key for this object as an iterator
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this iterator still valid?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return (current($this->headers) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the internal pointer for this object as an iterator
|
||||
*
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current value for this iterator, lazy loading it if need be
|
||||
*
|
||||
* @return Header\HeaderInterface
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$current = current($this->headers);
|
||||
if ($current instanceof Header\GenericHeader) {
|
||||
$current = $this->lazyLoadHeader(key($this->headers));
|
||||
}
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of headers in this contain, if all headers have not been parsed, actual count could
|
||||
* increase if MultipleHeader objects exist in the Request/Response. If you need an exact count, iterate
|
||||
*
|
||||
* @return int count of currently known headers
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all headers at once
|
||||
*
|
||||
* This method handles the normal iteration of headers; it is up to the
|
||||
* concrete classes to prepend with the appropriate status/request line.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
$headers = '';
|
||||
foreach ($this as $header) {
|
||||
if ($str = $header->toString()) {
|
||||
$headers .= $str . self::EOL;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the headers container as an array
|
||||
*
|
||||
* @param bool $format Return the values in Mime::Encoded or in Raw format
|
||||
* @return array
|
||||
* @todo determine how to produce single line headers, if they are supported
|
||||
*/
|
||||
public function toArray($format = Header\HeaderInterface::FORMAT_RAW)
|
||||
{
|
||||
$headers = [];
|
||||
/* @var $header Header\HeaderInterface */
|
||||
foreach ($this->headers as $header) {
|
||||
if ($header instanceof Header\MultipleHeadersInterface) {
|
||||
$name = $header->getFieldName();
|
||||
if (! isset($headers[$name])) {
|
||||
$headers[$name] = [];
|
||||
}
|
||||
$headers[$name][] = $header->getFieldValue($format);
|
||||
} else {
|
||||
$headers[$header->getFieldName()] = $header->getFieldValue($format);
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* By calling this, it will force parsing and loading of all headers, after this count() will be accurate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function forceLoading()
|
||||
{
|
||||
foreach ($this as $item) {
|
||||
// $item should now be loaded
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Header object from header line
|
||||
*
|
||||
* @param string $headerLine
|
||||
* @return Header\HeaderInterface|Header\HeaderInterface[]
|
||||
*/
|
||||
public function loadHeader($headerLine)
|
||||
{
|
||||
list($name, ) = Header\GenericHeader::splitHeaderLine($headerLine);
|
||||
|
||||
/** @var HeaderInterface $class */
|
||||
$class = $this->resolveHeaderClass($name);
|
||||
return $class::fromString($headerLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $index
|
||||
* @return mixed
|
||||
*/
|
||||
protected function lazyLoadHeader($index)
|
||||
{
|
||||
$current = $this->headers[$index];
|
||||
|
||||
$key = $this->headersKeys[$index];
|
||||
|
||||
/** @var GenericHeader $class */
|
||||
$class = $this->resolveHeaderClass($key);
|
||||
|
||||
$encoding = $current->getEncoding();
|
||||
$headers = $class::fromString($current->toString());
|
||||
if (is_array($headers)) {
|
||||
$current = array_shift($headers);
|
||||
$current->setEncoding($encoding);
|
||||
$this->headers[$index] = $current;
|
||||
foreach ($headers as $header) {
|
||||
$header->setEncoding($encoding);
|
||||
$this->headersKeys[] = $key;
|
||||
$this->headers[] = $header;
|
||||
}
|
||||
return $current;
|
||||
}
|
||||
|
||||
$current = $headers;
|
||||
$current->setEncoding($encoding);
|
||||
$this->headers[$index] = $current;
|
||||
return $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a field name
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeFieldName($fieldName)
|
||||
{
|
||||
return str_replace(['-', '_', ' ', '.'], '', strtolower($fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
private function resolveHeaderClass($key)
|
||||
{
|
||||
if ($this->pluginClassLoader) {
|
||||
return $this->pluginClassLoader->load($key) ?: Header\GenericHeader::class;
|
||||
}
|
||||
return $this->getHeaderLocator()->get($key, Header\GenericHeader::class);
|
||||
}
|
||||
}
|
||||
576
vendor/laminas/laminas-mail/src/Message.php
vendored
Normal file
576
vendor/laminas/laminas-mail/src/Message.php
vendored
Normal file
@ -0,0 +1,576 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
use Laminas\Mail\Header\ContentType;
|
||||
use Laminas\Mail\Header\Sender;
|
||||
use Laminas\Mime;
|
||||
use Traversable;
|
||||
|
||||
class Message
|
||||
{
|
||||
/**
|
||||
* Content of the message
|
||||
*
|
||||
* @var string|object|Mime\Message
|
||||
*/
|
||||
protected $body;
|
||||
|
||||
/**
|
||||
* @var Headers
|
||||
*/
|
||||
protected $headers;
|
||||
|
||||
/**
|
||||
* Message encoding
|
||||
*
|
||||
* Used to determine whether or not to encode headers; defaults to ASCII.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $encoding = 'ASCII';
|
||||
|
||||
/**
|
||||
* Is the message valid?
|
||||
*
|
||||
* If we don't any From addresses, we're invalid, according to RFC2822.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
$from = $this->getFrom();
|
||||
if (! $from instanceof AddressList) {
|
||||
return false;
|
||||
}
|
||||
return (bool) count($from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message encoding
|
||||
*
|
||||
* @param string $encoding
|
||||
* @return Message
|
||||
*/
|
||||
public function setEncoding($encoding)
|
||||
{
|
||||
$this->encoding = $encoding;
|
||||
$this->getHeaders()->setEncoding($encoding);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message encoding
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncoding()
|
||||
{
|
||||
return $this->encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose headers
|
||||
*
|
||||
* @param Headers $headers
|
||||
* @return Message
|
||||
*/
|
||||
public function setHeaders(Headers $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
$headers->setEncoding($this->getEncoding());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access headers collection
|
||||
*
|
||||
* Lazy-loads if not already attached.
|
||||
*
|
||||
* @return Headers
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
if (null === $this->headers) {
|
||||
$this->setHeaders(new Headers());
|
||||
$date = Header\Date::fromString('Date: ' . date('r'));
|
||||
$this->headers->addHeader($date);
|
||||
}
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set (overwrite) From addresses
|
||||
*
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressList
|
||||
* @param string|null $name
|
||||
* @return Message
|
||||
*/
|
||||
public function setFrom($emailOrAddressList, $name = null)
|
||||
{
|
||||
$this->clearHeaderByName('from');
|
||||
return $this->addFrom($emailOrAddressList, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "From" address
|
||||
*
|
||||
* @param string|Address|array|AddressList|Traversable $emailOrAddressOrList
|
||||
* @param string|null $name
|
||||
* @return Message
|
||||
*/
|
||||
public function addFrom($emailOrAddressOrList, $name = null)
|
||||
{
|
||||
$addressList = $this->getFrom();
|
||||
$this->updateAddressList($addressList, $emailOrAddressOrList, $name, __METHOD__);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve list of From senders
|
||||
*
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->getAddressListFromHeader('from', __NAMESPACE__ . '\Header\From');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the address list in the To recipients
|
||||
*
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressList
|
||||
* @param null|string $name
|
||||
* @return Message
|
||||
*/
|
||||
public function setTo($emailOrAddressList, $name = null)
|
||||
{
|
||||
$this->clearHeaderByName('to');
|
||||
return $this->addTo($emailOrAddressList, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more addresses to the To recipients
|
||||
*
|
||||
* Appends to the list.
|
||||
*
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressOrList
|
||||
* @param null|string $name
|
||||
* @return Message
|
||||
*/
|
||||
public function addTo($emailOrAddressOrList, $name = null)
|
||||
{
|
||||
$addressList = $this->getTo();
|
||||
$this->updateAddressList($addressList, $emailOrAddressOrList, $name, __METHOD__);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the address list of the To header
|
||||
*
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getTo()
|
||||
{
|
||||
return $this->getAddressListFromHeader('to', __NAMESPACE__ . '\Header\To');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set (overwrite) CC addresses
|
||||
*
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressList
|
||||
* @param string|null $name
|
||||
* @return Message
|
||||
*/
|
||||
public function setCc($emailOrAddressList, $name = null)
|
||||
{
|
||||
$this->clearHeaderByName('cc');
|
||||
return $this->addCc($emailOrAddressList, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "Cc" address
|
||||
*
|
||||
* @param string|Address|array|AddressList|Traversable $emailOrAddressOrList
|
||||
* @param string|null $name
|
||||
* @return Message
|
||||
*/
|
||||
public function addCc($emailOrAddressOrList, $name = null)
|
||||
{
|
||||
$addressList = $this->getCc();
|
||||
$this->updateAddressList($addressList, $emailOrAddressOrList, $name, __METHOD__);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve list of CC recipients
|
||||
*
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getCc()
|
||||
{
|
||||
return $this->getAddressListFromHeader('cc', __NAMESPACE__ . '\Header\Cc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set (overwrite) BCC addresses
|
||||
*
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressList
|
||||
* @param string|null $name
|
||||
* @return Message
|
||||
*/
|
||||
public function setBcc($emailOrAddressList, $name = null)
|
||||
{
|
||||
$this->clearHeaderByName('bcc');
|
||||
return $this->addBcc($emailOrAddressList, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "Bcc" address
|
||||
*
|
||||
* @param string|Address|array|AddressList|Traversable $emailOrAddressOrList
|
||||
* @param string|null $name
|
||||
* @return Message
|
||||
*/
|
||||
public function addBcc($emailOrAddressOrList, $name = null)
|
||||
{
|
||||
$addressList = $this->getBcc();
|
||||
$this->updateAddressList($addressList, $emailOrAddressOrList, $name, __METHOD__);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve list of BCC recipients
|
||||
*
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getBcc()
|
||||
{
|
||||
return $this->getAddressListFromHeader('bcc', __NAMESPACE__ . '\Header\Bcc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the address list in the Reply-To recipients
|
||||
*
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressList
|
||||
* @param null|string $name
|
||||
* @return Message
|
||||
*/
|
||||
public function setReplyTo($emailOrAddressList, $name = null)
|
||||
{
|
||||
$this->clearHeaderByName('reply-to');
|
||||
return $this->addReplyTo($emailOrAddressList, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more addresses to the Reply-To recipients
|
||||
*
|
||||
* Appends to the list.
|
||||
*
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressOrList
|
||||
* @param null|string $name
|
||||
* @return Message
|
||||
*/
|
||||
public function addReplyTo($emailOrAddressOrList, $name = null)
|
||||
{
|
||||
$addressList = $this->getReplyTo();
|
||||
$this->updateAddressList($addressList, $emailOrAddressOrList, $name, __METHOD__);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the address list of the Reply-To header
|
||||
*
|
||||
* @return AddressList
|
||||
*/
|
||||
public function getReplyTo()
|
||||
{
|
||||
return $this->getAddressListFromHeader('reply-to', __NAMESPACE__ . '\Header\ReplyTo');
|
||||
}
|
||||
|
||||
/**
|
||||
* setSender
|
||||
*
|
||||
* @param mixed $emailOrAddress
|
||||
* @param mixed $name
|
||||
* @return Message
|
||||
*/
|
||||
public function setSender($emailOrAddress, $name = null)
|
||||
{
|
||||
/** @var Sender $header */
|
||||
$header = $this->getHeaderByName('sender', __NAMESPACE__ . '\Header\Sender');
|
||||
$header->setAddress($emailOrAddress, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the sender address, if any
|
||||
*
|
||||
* @return null|Address\AddressInterface
|
||||
*/
|
||||
public function getSender()
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
if (! $headers->has('sender')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var Sender $header */
|
||||
$header = $this->getHeaderByName('sender', __NAMESPACE__ . '\Header\Sender');
|
||||
return $header->getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message subject header value
|
||||
*
|
||||
* @param string $subject
|
||||
* @return Message
|
||||
*/
|
||||
public function setSubject($subject)
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
if (! $headers->has('subject')) {
|
||||
$header = new Header\Subject();
|
||||
$headers->addHeader($header);
|
||||
} else {
|
||||
$header = $headers->get('subject');
|
||||
}
|
||||
$header->setSubject($subject);
|
||||
$header->setEncoding($this->getEncoding());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message subject header value
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
if (! $headers->has('subject')) {
|
||||
return;
|
||||
}
|
||||
$header = $headers->get('subject');
|
||||
return $header->getFieldValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the message body
|
||||
*
|
||||
* @param null|string|\Laminas\Mime\Message|object $body
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return Message
|
||||
*/
|
||||
public function setBody($body)
|
||||
{
|
||||
if (! is_string($body) && $body !== null) {
|
||||
if (! is_object($body)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a string or object argument; received "%s"',
|
||||
__METHOD__,
|
||||
gettype($body)
|
||||
));
|
||||
}
|
||||
if (! $body instanceof Mime\Message) {
|
||||
if (! method_exists($body, '__toString')) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects object arguments of type %s or implementing __toString();'
|
||||
. ' object of type "%s" received',
|
||||
__METHOD__,
|
||||
Mime\Message::class,
|
||||
get_class($body)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->body = $body;
|
||||
|
||||
if (! $this->body instanceof Mime\Message) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Get headers, and set Mime-Version header
|
||||
$headers = $this->getHeaders();
|
||||
$this->getHeaderByName('mime-version', __NAMESPACE__ . '\Header\MimeVersion');
|
||||
|
||||
// Multipart content headers
|
||||
if ($this->body->isMultiPart()) {
|
||||
$mime = $this->body->getMime();
|
||||
|
||||
/** @var ContentType $header */
|
||||
$header = $this->getHeaderByName('content-type', __NAMESPACE__ . '\Header\ContentType');
|
||||
$header->setType('multipart/mixed');
|
||||
$header->addParameter('boundary', $mime->boundary());
|
||||
return $this;
|
||||
}
|
||||
|
||||
// MIME single part headers
|
||||
$parts = $this->body->getParts();
|
||||
if (! empty($parts)) {
|
||||
$part = array_shift($parts);
|
||||
$headers->addHeaders($part->getHeadersArray("\r\n"));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently set message body
|
||||
*
|
||||
* @return object|string|Mime\Message
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string-serialized message body text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBodyText()
|
||||
{
|
||||
if ($this->body instanceof Mime\Message) {
|
||||
return $this->body->generateMessage(Headers::EOL);
|
||||
}
|
||||
|
||||
return (string) $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a header by name
|
||||
*
|
||||
* If not found, instantiates one based on $headerClass.
|
||||
*
|
||||
* @param string $headerName
|
||||
* @param string $headerClass
|
||||
* @return Header\HeaderInterface|\ArrayIterator header instance or collection of headers
|
||||
*/
|
||||
protected function getHeaderByName($headerName, $headerClass)
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
if ($headers->has($headerName)) {
|
||||
$header = $headers->get($headerName);
|
||||
} else {
|
||||
$header = new $headerClass();
|
||||
$headers->addHeader($header);
|
||||
}
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a header by name
|
||||
*
|
||||
* @param string $headerName
|
||||
*/
|
||||
protected function clearHeaderByName($headerName)
|
||||
{
|
||||
$this->getHeaders()->removeHeader($headerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the AddressList from a named header
|
||||
*
|
||||
* Used with To, From, Cc, Bcc, and ReplyTo headers. If the header does not
|
||||
* exist, instantiates it.
|
||||
*
|
||||
* @param string $headerName
|
||||
* @param string $headerClass
|
||||
* @throws Exception\DomainException
|
||||
* @return AddressList
|
||||
*/
|
||||
protected function getAddressListFromHeader($headerName, $headerClass)
|
||||
{
|
||||
$header = $this->getHeaderByName($headerName, $headerClass);
|
||||
if (! $header instanceof Header\AbstractAddressList) {
|
||||
throw new Exception\DomainException(sprintf(
|
||||
'Cannot grab address list from header of type "%s"; not an AbstractAddressList implementation',
|
||||
get_class($header)
|
||||
));
|
||||
}
|
||||
return $header->getAddressList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an address list
|
||||
*
|
||||
* Proxied to this from addFrom, addTo, addCc, addBcc, and addReplyTo.
|
||||
*
|
||||
* @param AddressList $addressList
|
||||
* @param string|Address\AddressInterface|array|AddressList|Traversable $emailOrAddressOrList
|
||||
* @param null|string $name
|
||||
* @param string $callingMethod
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function updateAddressList(AddressList $addressList, $emailOrAddressOrList, $name, $callingMethod)
|
||||
{
|
||||
if ($emailOrAddressOrList instanceof Traversable) {
|
||||
foreach ($emailOrAddressOrList as $address) {
|
||||
$addressList->add($address);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (is_array($emailOrAddressOrList)) {
|
||||
$addressList->addMany($emailOrAddressOrList);
|
||||
return;
|
||||
}
|
||||
if (! is_string($emailOrAddressOrList) && ! $emailOrAddressOrList instanceof Address\AddressInterface) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a string, AddressInterface, array, AddressList, or Traversable as its first argument;'
|
||||
. ' received "%s"',
|
||||
$callingMethod,
|
||||
(is_object($emailOrAddressOrList) ? get_class($emailOrAddressOrList) : gettype($emailOrAddressOrList))
|
||||
));
|
||||
}
|
||||
|
||||
if (is_string($emailOrAddressOrList) && $name === null) {
|
||||
$addressList->addFromString($emailOrAddressOrList);
|
||||
return;
|
||||
}
|
||||
|
||||
$addressList->add($emailOrAddressOrList, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize to string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString()
|
||||
{
|
||||
$headers = $this->getHeaders();
|
||||
return $headers->toString()
|
||||
. Headers::EOL
|
||||
. $this->getBodyText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate from raw message string
|
||||
*
|
||||
* @todo Restore body to Mime\Message
|
||||
* @param string $rawMessage
|
||||
* @return Message
|
||||
*/
|
||||
public static function fromString($rawMessage)
|
||||
{
|
||||
$message = new static();
|
||||
|
||||
/** @var Headers $headers */
|
||||
$headers = null;
|
||||
$content = null;
|
||||
Mime\Decode::splitMessage($rawMessage, $headers, $content, Headers::EOL);
|
||||
if ($headers->has('mime-version')) {
|
||||
// todo - restore body to mime\message
|
||||
}
|
||||
$message->setHeaders($headers);
|
||||
$message->setBody($content);
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
64
vendor/laminas/laminas-mail/src/MessageFactory.php
vendored
Normal file
64
vendor/laminas/laminas-mail/src/MessageFactory.php
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
use Traversable;
|
||||
|
||||
class MessageFactory
|
||||
{
|
||||
/**
|
||||
* @param array|Traversable $options
|
||||
* @return Message
|
||||
*/
|
||||
public static function getInstance($options = [])
|
||||
{
|
||||
if (! is_array($options) && ! $options instanceof Traversable) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'"%s" expects an array or Traversable; received "%s"',
|
||||
__METHOD__,
|
||||
(is_object($options) ? get_class($options) : gettype($options))
|
||||
));
|
||||
}
|
||||
|
||||
$message = new Message();
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
$setter = self::getSetterMethod($key);
|
||||
if (method_exists($message, $setter)) {
|
||||
$message->{$setter}($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a setter method name based on a provided key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
private static function getSetterMethod($key)
|
||||
{
|
||||
return 'set'
|
||||
. str_replace(
|
||||
' ',
|
||||
'',
|
||||
ucwords(
|
||||
strtr(
|
||||
$key,
|
||||
[
|
||||
'-' => ' ',
|
||||
'_' => ' ',
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
25
vendor/laminas/laminas-mail/src/Module.php
vendored
Normal file
25
vendor/laminas/laminas-mail/src/Module.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
class Module
|
||||
{
|
||||
/**
|
||||
* Retrieve laminas-mail package configuration for laminas-mvc context.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$provider = new ConfigProvider();
|
||||
return [
|
||||
'service_manager' => $provider->getDependencyConfig(),
|
||||
];
|
||||
}
|
||||
}
|
||||
356
vendor/laminas/laminas-mail/src/Protocol/AbstractProtocol.php
vendored
Normal file
356
vendor/laminas/laminas-mail/src/Protocol/AbstractProtocol.php
vendored
Normal file
@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol;
|
||||
|
||||
use Laminas\Validator;
|
||||
|
||||
/**
|
||||
* Provides low-level methods for concrete adapters to communicate with a
|
||||
* remote mail server and track requests and responses.
|
||||
*
|
||||
* @todo Implement proxy settings
|
||||
*/
|
||||
abstract class AbstractProtocol
|
||||
{
|
||||
/**
|
||||
* Mail default EOL string
|
||||
*/
|
||||
const EOL = "\r\n";
|
||||
|
||||
/**
|
||||
* Default timeout in seconds for initiating session
|
||||
*/
|
||||
const TIMEOUT_CONNECTION = 30;
|
||||
|
||||
/**
|
||||
* Maximum of the transaction log
|
||||
* @var int
|
||||
*/
|
||||
protected $maximumLog = 64;
|
||||
|
||||
/**
|
||||
* Hostname or IP address of remote server
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* Port number of connection
|
||||
* @var int
|
||||
*/
|
||||
protected $port;
|
||||
|
||||
/**
|
||||
* Instance of Laminas\Validator\ValidatorChain to check hostnames
|
||||
* @var \Laminas\Validator\ValidatorChain
|
||||
*/
|
||||
protected $validHost;
|
||||
|
||||
/**
|
||||
* Socket connection resource
|
||||
* @var null|resource
|
||||
*/
|
||||
protected $socket;
|
||||
|
||||
/**
|
||||
* Last request sent to server
|
||||
* @var string
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Array of server responses to last request
|
||||
* @var array
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Log of mail requests and server responses for a session
|
||||
* @var array
|
||||
*/
|
||||
private $log = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $host OPTIONAL Hostname of remote connection (default: 127.0.0.1)
|
||||
* @param int $port OPTIONAL Port number (default: null)
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function __construct($host = '127.0.0.1', $port = null)
|
||||
{
|
||||
$this->validHost = new Validator\ValidatorChain();
|
||||
$this->validHost->attach(new Validator\Hostname(Validator\Hostname::ALLOW_ALL));
|
||||
|
||||
if (! $this->validHost->isValid($host)) {
|
||||
throw new Exception\RuntimeException(implode(', ', $this->validHost->getMessages()));
|
||||
}
|
||||
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class destructor to cleanup open resources
|
||||
*
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->_disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum log size
|
||||
*
|
||||
* @param int $maximumLog Maximum log size
|
||||
*/
|
||||
public function setMaximumLog($maximumLog)
|
||||
{
|
||||
$this->maximumLog = (int) $maximumLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum log size
|
||||
*
|
||||
* @return int the maximum log size
|
||||
*/
|
||||
public function getMaximumLog()
|
||||
{
|
||||
return $this->maximumLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection to the remote host
|
||||
*
|
||||
* Concrete adapters for this class will implement their own unique connect
|
||||
* scripts, using the _connect() method to create the socket resource.
|
||||
*/
|
||||
abstract public function connect();
|
||||
|
||||
/**
|
||||
* Retrieve the last client request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last server response
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the transaction log
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLog()
|
||||
{
|
||||
return implode('', $this->log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the transaction log
|
||||
*
|
||||
*/
|
||||
public function resetLog()
|
||||
{
|
||||
$this->log = [];
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* Add the transaction log
|
||||
*
|
||||
* @param string $value new transaction
|
||||
*/
|
||||
protected function _addLog($value)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
if ($this->maximumLog >= 0 && count($this->log) >= $this->maximumLog) {
|
||||
array_shift($this->log);
|
||||
}
|
||||
|
||||
$this->log[] = $value;
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* Connect to the server using the supplied transport and target
|
||||
*
|
||||
* An example $remote string may be 'tcp://mail.example.com:25' or 'ssh://hostname.com:2222'
|
||||
*
|
||||
* @deprecated Since 1.12.0. Implementations should use the ProtocolTrait::setupSocket() method instead.
|
||||
* @todo Remove for 3.0.0.
|
||||
* @param string $remote Remote
|
||||
* @throws Exception\RuntimeException
|
||||
* @return bool
|
||||
*/
|
||||
protected function _connect($remote)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
$errorNum = 0;
|
||||
$errorStr = '';
|
||||
|
||||
// open connection
|
||||
set_error_handler(
|
||||
function ($error, $message = '') {
|
||||
throw new Exception\RuntimeException(sprintf('Could not open socket: %s', $message), $error);
|
||||
},
|
||||
E_WARNING
|
||||
);
|
||||
$this->socket = stream_socket_client($remote, $errorNum, $errorStr, self::TIMEOUT_CONNECTION);
|
||||
restore_error_handler();
|
||||
|
||||
if ($this->socket === false) {
|
||||
if ($errorNum == 0) {
|
||||
$errorStr = 'Could not open socket';
|
||||
}
|
||||
throw new Exception\RuntimeException($errorStr);
|
||||
}
|
||||
|
||||
if (($result = stream_set_timeout($this->socket, self::TIMEOUT_CONNECTION)) === false) {
|
||||
throw new Exception\RuntimeException('Could not set stream timeout');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* Disconnect from remote host and free resource
|
||||
*
|
||||
*/
|
||||
protected function _disconnect()
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
if (is_resource($this->socket)) {
|
||||
fclose($this->socket);
|
||||
}
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* Send the given request followed by a LINEEND to the server.
|
||||
*
|
||||
* @param string $request
|
||||
* @throws Exception\RuntimeException
|
||||
* @return int|bool Number of bytes written to remote host
|
||||
*/
|
||||
protected function _send($request)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
if (! is_resource($this->socket)) {
|
||||
throw new Exception\RuntimeException('No connection has been established to ' . $this->host);
|
||||
}
|
||||
|
||||
$this->request = $request;
|
||||
|
||||
$result = fwrite($this->socket, $request . self::EOL);
|
||||
|
||||
// Save request to internal log
|
||||
$this->_addLog($request . self::EOL);
|
||||
|
||||
if ($result === false) {
|
||||
throw new Exception\RuntimeException('Could not send request to ' . $this->host);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* Get a line from the stream.
|
||||
*
|
||||
* @param int $timeout Per-request timeout value if applicable
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
protected function _receive($timeout = null)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
if (! is_resource($this->socket)) {
|
||||
throw new Exception\RuntimeException('No connection has been established to ' . $this->host);
|
||||
}
|
||||
|
||||
// Adapters may wish to supply per-commend timeouts according to appropriate RFC
|
||||
if ($timeout !== null) {
|
||||
stream_set_timeout($this->socket, $timeout);
|
||||
}
|
||||
|
||||
// Retrieve response
|
||||
$response = fgets($this->socket, 1024);
|
||||
|
||||
// Save request to internal log
|
||||
$this->_addLog($response);
|
||||
|
||||
// Check meta data to ensure connection is still valid
|
||||
$info = stream_get_meta_data($this->socket);
|
||||
|
||||
if (! empty($info['timed_out'])) {
|
||||
throw new Exception\RuntimeException($this->host . ' has timed out');
|
||||
}
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception\RuntimeException('Could not read from ' . $this->host);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* Parse server response for successful codes
|
||||
*
|
||||
* Read the response from the stream and check for expected return code.
|
||||
* Throws a Laminas\Mail\Protocol\Exception\ExceptionInterface if an unexpected code is returned.
|
||||
*
|
||||
* @param string|array $code One or more codes that indicate a successful response
|
||||
* @param int $timeout Per-request timeout value if applicable
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string Last line of response string
|
||||
*/
|
||||
protected function _expect($code, $timeout = null)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
$this->response = [];
|
||||
$errMsg = '';
|
||||
|
||||
if (! is_array($code)) {
|
||||
$code = [$code];
|
||||
}
|
||||
|
||||
do {
|
||||
$this->response[] = $result = $this->_receive($timeout);
|
||||
list($cmd, $more, $msg) = preg_split('/([\s-]+)/', $result, 2, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
if ($errMsg !== '') {
|
||||
$errMsg .= ' ' . $msg;
|
||||
} elseif ($cmd === null || ! in_array($cmd, $code)) {
|
||||
$errMsg = $msg;
|
||||
}
|
||||
|
||||
// The '-' message prefix indicates an information string instead of a response string.
|
||||
} while (strpos($more, '-') === 0);
|
||||
|
||||
if ($errMsg !== '') {
|
||||
throw new Exception\RuntimeException($errMsg);
|
||||
}
|
||||
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Protocol/Exception/ExceptionInterface.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Protocol/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol\Exception;
|
||||
|
||||
use Laminas\Mail\Exception\ExceptionInterface as MailException;
|
||||
|
||||
interface ExceptionInterface extends MailException
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Protocol/Exception/InvalidArgumentException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Protocol/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class InvalidArgumentException extends Exception\InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Protocol/Exception/RuntimeException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Protocol/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class RuntimeException extends Exception\RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
824
vendor/laminas/laminas-mail/src/Protocol/Imap.php
vendored
Normal file
824
vendor/laminas/laminas-mail/src/Protocol/Imap.php
vendored
Normal file
@ -0,0 +1,824 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol;
|
||||
|
||||
class Imap
|
||||
{
|
||||
use ProtocolTrait;
|
||||
|
||||
/**
|
||||
* Default timeout in seconds for initiating session
|
||||
*/
|
||||
const TIMEOUT_CONNECTION = 30;
|
||||
|
||||
/**
|
||||
* @var null|resource
|
||||
*/
|
||||
protected $socket;
|
||||
|
||||
/**
|
||||
* counter for request tag
|
||||
* @var int
|
||||
*/
|
||||
protected $tagCount = 0;
|
||||
|
||||
/**
|
||||
* Public constructor
|
||||
*
|
||||
* @param string $host hostname or IP address of IMAP server, if given connect() is called
|
||||
* @param int|null $port port of IMAP server, null for default (143 or 993 for ssl)
|
||||
* @param bool $ssl use ssl? 'SSL', 'TLS' or false
|
||||
* @param bool $novalidatecert set to true to skip SSL certificate validation
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function __construct($host = '', $port = null, $ssl = false, $novalidatecert = false)
|
||||
{
|
||||
$this->setNoValidateCert($novalidatecert);
|
||||
|
||||
if ($host) {
|
||||
$this->connect($host, $port, $ssl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open connection to IMAP server
|
||||
*
|
||||
* @param string $host hostname or IP address of IMAP server
|
||||
* @param int|null $port of IMAP server, default is 143 (993 for ssl)
|
||||
* @param string|bool $ssl use 'SSL', 'TLS' or false
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string welcome message
|
||||
*/
|
||||
public function connect($host, $port = null, $ssl = false)
|
||||
{
|
||||
$transport = 'tcp';
|
||||
$isTls = false;
|
||||
|
||||
if ($ssl) {
|
||||
$ssl = strtolower($ssl);
|
||||
}
|
||||
|
||||
switch ($ssl) {
|
||||
case 'ssl':
|
||||
$transport = 'ssl';
|
||||
if (! $port) {
|
||||
$port = 993;
|
||||
}
|
||||
break;
|
||||
case 'tls':
|
||||
$isTls = true;
|
||||
// break intentionally omitted
|
||||
default:
|
||||
if (! $port) {
|
||||
$port = 143;
|
||||
}
|
||||
}
|
||||
|
||||
$this->socket = $this->setupSocket($transport, $host, $port, self::TIMEOUT_CONNECTION);
|
||||
|
||||
if (! $this->assumedNextLine('* OK')) {
|
||||
throw new Exception\RuntimeException('host doesn\'t allow connection');
|
||||
}
|
||||
|
||||
if ($isTls) {
|
||||
$result = $this->requestAndResponse('STARTTLS');
|
||||
$result = $result && stream_socket_enable_crypto($this->socket, true, $this->getCryptoMethod());
|
||||
if (! $result) {
|
||||
throw new Exception\RuntimeException('cannot enable TLS');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the next line from socket with error checking, but nothing else
|
||||
*
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string next line
|
||||
*/
|
||||
protected function nextLine()
|
||||
{
|
||||
$line = fgets($this->socket);
|
||||
if ($line === false) {
|
||||
throw new Exception\RuntimeException('cannot read - connection closed?');
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* get next line and assume it starts with $start. some requests give a simple
|
||||
* feedback so we can quickly check if we can go on.
|
||||
*
|
||||
* @param string $start the first bytes we assume to be in the next line
|
||||
* @return bool line starts with $start
|
||||
*/
|
||||
protected function assumedNextLine($start)
|
||||
{
|
||||
$line = $this->nextLine();
|
||||
return strpos($line, $start) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get next line and split the tag. that's the normal case for a response line
|
||||
*
|
||||
* @param string $tag tag of line is returned by reference
|
||||
* @return string next line
|
||||
*/
|
||||
protected function nextTaggedLine(&$tag)
|
||||
{
|
||||
$line = $this->nextLine();
|
||||
|
||||
// separate tag from line
|
||||
list($tag, $line) = explode(' ', $line, 2);
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* split a given line in tokens. a token is literal of any form or a list
|
||||
*
|
||||
* @param string $line line to decode
|
||||
* @return array tokens, literals are returned as string, lists as array
|
||||
*/
|
||||
protected function decodeLine($line)
|
||||
{
|
||||
$tokens = [];
|
||||
$stack = [];
|
||||
|
||||
/*
|
||||
We start to decode the response here. The understood tokens are:
|
||||
literal
|
||||
"literal" or also "lit\\er\"al"
|
||||
{bytes}<NL>literal
|
||||
(literals*)
|
||||
All tokens are returned in an array. Literals in braces (the last understood
|
||||
token in the list) are returned as an array of tokens. I.e. the following response:
|
||||
"foo" baz {3}<NL>bar ("f\\\"oo" bar)
|
||||
would be returned as:
|
||||
array('foo', 'baz', 'bar', array('f\\\"oo', 'bar'));
|
||||
|
||||
// TODO: add handling of '[' and ']' to parser for easier handling of response text
|
||||
*/
|
||||
// replace any trailing <NL> including spaces with a single space
|
||||
$line = rtrim($line) . ' ';
|
||||
while (($pos = strpos($line, ' ')) !== false) {
|
||||
$token = substr($line, 0, $pos);
|
||||
if (! strlen($token)) {
|
||||
continue;
|
||||
}
|
||||
while ($token[0] == '(') {
|
||||
array_push($stack, $tokens);
|
||||
$tokens = [];
|
||||
$token = substr($token, 1);
|
||||
}
|
||||
if ($token[0] == '"') {
|
||||
if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) {
|
||||
$tokens[] = $matches[1];
|
||||
$line = substr($line, strlen($matches[0]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($token[0] == '{') {
|
||||
$endPos = strpos($token, '}');
|
||||
$chars = substr($token, 1, $endPos - 1);
|
||||
if (is_numeric($chars)) {
|
||||
$token = '';
|
||||
while (strlen($token) < $chars) {
|
||||
$token .= $this->nextLine();
|
||||
}
|
||||
$line = '';
|
||||
if (strlen($token) > $chars) {
|
||||
$line = substr($token, $chars);
|
||||
$token = substr($token, 0, $chars);
|
||||
} else {
|
||||
$line .= $this->nextLine();
|
||||
}
|
||||
$tokens[] = $token;
|
||||
$line = trim($line) . ' ';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($stack && $token[strlen($token) - 1] == ')') {
|
||||
// closing braces are not separated by spaces, so we need to count them
|
||||
$braces = strlen($token);
|
||||
$token = rtrim($token, ')');
|
||||
// only count braces if more than one
|
||||
$braces -= strlen($token) + 1;
|
||||
// only add if token had more than just closing braces
|
||||
if (rtrim($token) != '') {
|
||||
$tokens[] = rtrim($token);
|
||||
}
|
||||
$token = $tokens;
|
||||
$tokens = array_pop($stack);
|
||||
// special handline if more than one closing brace
|
||||
while ($braces-- > 0) {
|
||||
$tokens[] = $token;
|
||||
$token = $tokens;
|
||||
$tokens = array_pop($stack);
|
||||
}
|
||||
}
|
||||
$tokens[] = $token;
|
||||
$line = substr($line, $pos + 1);
|
||||
}
|
||||
|
||||
// maybe the server forgot to send some closing braces
|
||||
while ($stack) {
|
||||
$child = $tokens;
|
||||
$tokens = array_pop($stack);
|
||||
$tokens[] = $child;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* read a response "line" (could also be more than one real line if response has {..}<NL>)
|
||||
* and do a simple decode
|
||||
*
|
||||
* @param array|string $tokens decoded tokens are returned by reference, if $dontParse
|
||||
* is true the unparsed line is returned here
|
||||
* @param string $wantedTag check for this tag for response code. Default '*' is
|
||||
* continuation tag.
|
||||
* @param bool $dontParse if true only the unparsed line is returned $tokens
|
||||
* @return bool if returned tag matches wanted tag
|
||||
*/
|
||||
public function readLine(&$tokens = [], $wantedTag = '*', $dontParse = false)
|
||||
{
|
||||
$tag = null; // define $tag variable before first use
|
||||
$line = $this->nextTaggedLine($tag); // get next tag
|
||||
if (! $dontParse) {
|
||||
$tokens = $this->decodeLine($line);
|
||||
} else {
|
||||
$tokens = $line;
|
||||
}
|
||||
|
||||
// if tag is wanted tag we might be at the end of a multiline response
|
||||
return $tag == $wantedTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* read all lines of response until given tag is found (last line of response)
|
||||
*
|
||||
* @param string $tag the tag of your request
|
||||
* @param bool $dontParse if true every line is returned unparsed instead of
|
||||
* the decoded tokens
|
||||
* @return null|bool|array tokens if success, false if error, null if bad request
|
||||
*/
|
||||
public function readResponse($tag, $dontParse = false)
|
||||
{
|
||||
$lines = [];
|
||||
$tokens = null; // define $tokens variable before first use
|
||||
while (! $this->readLine($tokens, $tag, $dontParse)) {
|
||||
$lines[] = $tokens;
|
||||
}
|
||||
|
||||
if ($dontParse) {
|
||||
// last to chars are still needed for response code
|
||||
$tokens = [substr($tokens, 0, 2)];
|
||||
}
|
||||
// last line has response code
|
||||
if ($tokens[0] == 'OK') {
|
||||
return $lines ? $lines : true;
|
||||
} elseif ($tokens[0] == 'NO') {
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* send a request
|
||||
*
|
||||
* @param string $command your request command
|
||||
* @param array $tokens additional parameters to command, use escapeString() to prepare
|
||||
* @param string $tag provide a tag otherwise an autogenerated is returned
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function sendRequest($command, $tokens = [], &$tag = null)
|
||||
{
|
||||
if (! $tag) {
|
||||
++$this->tagCount;
|
||||
$tag = 'TAG' . $this->tagCount;
|
||||
}
|
||||
|
||||
$line = $tag . ' ' . $command;
|
||||
|
||||
foreach ($tokens as $token) {
|
||||
if (is_array($token)) {
|
||||
if (fwrite($this->socket, $line . ' ' . $token[0] . "\r\n") === false) {
|
||||
throw new Exception\RuntimeException('cannot write - connection closed?');
|
||||
}
|
||||
if (! $this->assumedNextLine('+ ')) {
|
||||
throw new Exception\RuntimeException('cannot send literal string');
|
||||
}
|
||||
$line = $token[1];
|
||||
} else {
|
||||
$line .= ' ' . $token;
|
||||
}
|
||||
}
|
||||
|
||||
if (fwrite($this->socket, $line . "\r\n") === false) {
|
||||
throw new Exception\RuntimeException('cannot write - connection closed?');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send a request and get response at once
|
||||
*
|
||||
* @param string $command command as in sendRequest()
|
||||
* @param array $tokens parameters as in sendRequest()
|
||||
* @param bool $dontParse if true unparsed lines are returned instead of tokens
|
||||
* @return mixed response as in readResponse()
|
||||
*/
|
||||
public function requestAndResponse($command, $tokens = [], $dontParse = false)
|
||||
{
|
||||
$tag = null; // define $tag variable before first use
|
||||
$this->sendRequest($command, $tokens, $tag);
|
||||
$response = $this->readResponse($tag, $dontParse);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* escape one or more literals i.e. for sendRequest
|
||||
*
|
||||
* @param string|array $string the literal/-s
|
||||
* @return string|array escape literals, literals with newline ar returned
|
||||
* as array('{size}', 'string');
|
||||
*/
|
||||
public function escapeString($string)
|
||||
{
|
||||
if (func_num_args() < 2) {
|
||||
if (strpos($string, "\n") !== false) {
|
||||
return ['{' . strlen($string) . '}', $string];
|
||||
} else {
|
||||
return '"' . str_replace(['\\', '"'], ['\\\\', '\\"'], $string) . '"';
|
||||
}
|
||||
}
|
||||
$result = [];
|
||||
foreach (func_get_args() as $string) {
|
||||
$result[] = $this->escapeString($string);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* escape a list with literals or lists
|
||||
*
|
||||
* @param array $list list with literals or lists as PHP array
|
||||
* @return string escaped list for imap
|
||||
*/
|
||||
public function escapeList($list)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($list as $v) {
|
||||
if (! is_array($v)) {
|
||||
$result[] = $v;
|
||||
continue;
|
||||
}
|
||||
$result[] = $this->escapeList($v);
|
||||
}
|
||||
return '(' . implode(' ', $result) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to IMAP server.
|
||||
*
|
||||
* @param string $user username
|
||||
* @param string $password password
|
||||
* @return bool success
|
||||
*/
|
||||
public function login($user, $password)
|
||||
{
|
||||
return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* logout of imap server
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
$result = false;
|
||||
if ($this->socket) {
|
||||
try {
|
||||
$result = $this->requestAndResponse('LOGOUT', [], true);
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
// ignoring exception
|
||||
}
|
||||
fclose($this->socket);
|
||||
$this->socket = null;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capabilities from IMAP server
|
||||
*
|
||||
* @return array list of capabilities
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function capability()
|
||||
{
|
||||
$response = $this->requestAndResponse('CAPABILITY');
|
||||
|
||||
if (! $response) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$capabilities = [];
|
||||
foreach ($response as $line) {
|
||||
$capabilities = array_merge($capabilities, $line);
|
||||
}
|
||||
return $capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine and select have the same response. The common code for both
|
||||
* is in this method
|
||||
*
|
||||
* @param string $command can be 'EXAMINE' or 'SELECT' and this is used as command
|
||||
* @param string $box which folder to change to or examine
|
||||
* @return bool|array false if error, array with returned information
|
||||
* otherwise (flags, exists, recent, uidvalidity)
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX')
|
||||
{
|
||||
$tag = null; // define $tag variable before first use
|
||||
$this->sendRequest($command, [$this->escapeString($box)], $tag);
|
||||
|
||||
$result = [];
|
||||
$tokens = null; // define $tokens variable before first use
|
||||
while (! $this->readLine($tokens, $tag)) {
|
||||
if ($tokens[0] == 'FLAGS') {
|
||||
array_shift($tokens);
|
||||
$result['flags'] = $tokens;
|
||||
continue;
|
||||
}
|
||||
switch ($tokens[1]) {
|
||||
case 'EXISTS':
|
||||
case 'RECENT':
|
||||
$result[strtolower($tokens[1])] = $tokens[0];
|
||||
break;
|
||||
case '[UIDVALIDITY':
|
||||
$result['uidvalidity'] = (int) $tokens[2];
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if ($tokens[0] != 'OK') {
|
||||
return false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* change folder
|
||||
*
|
||||
* @param string $box change to this folder
|
||||
* @return bool|array see examineOrselect()
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function select($box = 'INBOX')
|
||||
{
|
||||
return $this->examineOrSelect('SELECT', $box);
|
||||
}
|
||||
|
||||
/**
|
||||
* examine folder
|
||||
*
|
||||
* @param string $box examine this folder
|
||||
* @return bool|array see examineOrselect()
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function examine($box = 'INBOX')
|
||||
{
|
||||
return $this->examineOrSelect('EXAMINE', $box);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch one or more items of one or more messages
|
||||
*
|
||||
* @param string|array $items items to fetch from message(s) as string (if only one item)
|
||||
* or array of strings
|
||||
* @param int|array $from message for items or start message if $to !== null
|
||||
* @param int|null $to if null only one message ($from) is fetched, else it's the
|
||||
* last message, INF means last message available
|
||||
* @param bool $uid set to true if passing a unique id
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string|array if only one item of one message is fetched it's returned as string
|
||||
* if items of one message are fetched it's returned as (name => value)
|
||||
* if one items of messages are fetched it's returned as (msgno => value)
|
||||
* if items of messages are fetched it's returned as (msgno => (name => value))
|
||||
*/
|
||||
public function fetch($items, $from, $to = null, $uid = false)
|
||||
{
|
||||
if (is_array($from)) {
|
||||
$set = implode(',', $from);
|
||||
} elseif ($to === null) {
|
||||
$set = (int) $from;
|
||||
} elseif ($to === INF) {
|
||||
$set = (int) $from . ':*';
|
||||
} else {
|
||||
$set = (int) $from . ':' . (int) $to;
|
||||
}
|
||||
|
||||
$items = (array) $items;
|
||||
$itemList = $this->escapeList($items);
|
||||
|
||||
$tag = null; // define $tag variable before first use
|
||||
$this->sendRequest(($uid ? 'UID ' : '') . 'FETCH', [$set, $itemList], $tag);
|
||||
|
||||
$result = [];
|
||||
$tokens = null; // define $tokens variable before first use
|
||||
while (! $this->readLine($tokens, $tag)) {
|
||||
// ignore other responses
|
||||
if ($tokens[1] != 'FETCH') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// find array key of UID value; try the last elements, or search for it
|
||||
if ($uid) {
|
||||
$count = count($tokens[2]);
|
||||
if ($tokens[2][$count - 2] == 'UID') {
|
||||
$uidKey = $count - 1;
|
||||
} else {
|
||||
$uidKey = array_search('UID', $tokens[2]) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ignore other messages
|
||||
if ($to === null && ! is_array($from) && ($uid ? $tokens[2][$uidKey] != $from : $tokens[0] != $from)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we only want one item we return that one directly
|
||||
if (count($items) == 1) {
|
||||
if ($tokens[2][0] == $items[0]) {
|
||||
$data = $tokens[2][1];
|
||||
} elseif ($uid && $tokens[2][2] == $items[0]) {
|
||||
$data = $tokens[2][3];
|
||||
} else {
|
||||
// maybe the server send an other field we didn't wanted
|
||||
$count = count($tokens[2]);
|
||||
// we start with 2, because 0 was already checked
|
||||
for ($i = 2; $i < $count; $i += 2) {
|
||||
if ($tokens[2][$i] != $items[0]) {
|
||||
continue;
|
||||
}
|
||||
$data = $tokens[2][$i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data = [];
|
||||
while (key($tokens[2]) !== null) {
|
||||
$data[current($tokens[2])] = next($tokens[2]);
|
||||
next($tokens[2]);
|
||||
}
|
||||
}
|
||||
|
||||
// if we want only one message we can ignore everything else and just return
|
||||
if ($to === null && ! is_array($from) && ($uid ? $tokens[2][$uidKey] == $from : $tokens[0] == $from)) {
|
||||
// we still need to read all lines
|
||||
while (! $this->readLine($tokens, $tag)) {
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
$result[$tokens[0]] = $data;
|
||||
}
|
||||
|
||||
if ($to === null && ! is_array($from)) {
|
||||
throw new Exception\RuntimeException('the single id was not found in response');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* get mailbox list
|
||||
*
|
||||
* this method can't be named after the IMAP command 'LIST', as list is a reserved keyword
|
||||
*
|
||||
* @param string $reference mailbox reference for list
|
||||
* @param string $mailbox mailbox name match with wildcards
|
||||
* @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..))
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function listMailbox($reference = '', $mailbox = '*')
|
||||
{
|
||||
$result = [];
|
||||
$list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox));
|
||||
if (! $list || $list === true) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ($list as $item) {
|
||||
if (count($item) != 4 || $item[0] != 'LIST') {
|
||||
continue;
|
||||
}
|
||||
$result[$item[3]] = ['delim' => $item[2], 'flags' => $item[1]];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* set flags
|
||||
*
|
||||
* @param array $flags flags to set, add or remove - see $mode
|
||||
* @param int $from message for items or start message if $to !== null
|
||||
* @param int|null $to if null only one message ($from) is fetched, else it's the
|
||||
* last message, INF means last message available
|
||||
* @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
|
||||
* @param bool $silent if false the return values are the new flags for the wanted messages
|
||||
* @return bool|array new flags if $silent is false, else true or false depending on success
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function store(array $flags, $from, $to = null, $mode = null, $silent = true)
|
||||
{
|
||||
$item = 'FLAGS';
|
||||
if ($mode == '+' || $mode == '-') {
|
||||
$item = $mode . $item;
|
||||
}
|
||||
if ($silent) {
|
||||
$item .= '.SILENT';
|
||||
}
|
||||
|
||||
$flags = $this->escapeList($flags);
|
||||
$set = (int) $from;
|
||||
if ($to !== null) {
|
||||
$set .= ':' . ($to == INF ? '*' : (int) $to);
|
||||
}
|
||||
|
||||
$result = $this->requestAndResponse('STORE', [$set, $item, $flags], $silent);
|
||||
|
||||
if ($silent) {
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
$tokens = $result;
|
||||
$result = [];
|
||||
foreach ($tokens as $token) {
|
||||
if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') {
|
||||
continue;
|
||||
}
|
||||
$result[$token[0]] = $token[2][1];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* append a new message to given folder
|
||||
*
|
||||
* @param string $folder name of target folder
|
||||
* @param string $message full message content
|
||||
* @param array $flags flags for new message
|
||||
* @param string $date date for new message
|
||||
* @return bool success
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function append($folder, $message, $flags = null, $date = null)
|
||||
{
|
||||
$tokens = [];
|
||||
$tokens[] = $this->escapeString($folder);
|
||||
if ($flags !== null) {
|
||||
$tokens[] = $this->escapeList($flags);
|
||||
}
|
||||
if ($date !== null) {
|
||||
$tokens[] = $this->escapeString($date);
|
||||
}
|
||||
$tokens[] = $this->escapeString($message);
|
||||
|
||||
return $this->requestAndResponse('APPEND', $tokens, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* copy message set from current folder to other folder
|
||||
*
|
||||
* @param string $folder destination folder
|
||||
* @param $from
|
||||
* @param int|null $to if null only one message ($from) is fetched, else it's the
|
||||
* last message, INF means last message available
|
||||
* @return bool success
|
||||
*/
|
||||
public function copy($folder, $from, $to = null)
|
||||
{
|
||||
$set = (int) $from;
|
||||
if ($to !== null) {
|
||||
$set .= ':' . ($to == INF ? '*' : (int) $to);
|
||||
}
|
||||
|
||||
return $this->requestAndResponse('COPY', [$set, $this->escapeString($folder)], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new folder (and parent folders if needed)
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @return bool success
|
||||
*/
|
||||
public function create($folder)
|
||||
{
|
||||
return $this->requestAndResponse('CREATE', [$this->escapeString($folder)], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* rename an existing folder
|
||||
*
|
||||
* @param string $old old name
|
||||
* @param string $new new name
|
||||
* @return bool success
|
||||
*/
|
||||
public function rename($old, $new)
|
||||
{
|
||||
return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a folder
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @return bool success
|
||||
*/
|
||||
public function delete($folder)
|
||||
{
|
||||
return $this->requestAndResponse('DELETE', [$this->escapeString($folder)], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* subscribe to a folder
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @return bool success
|
||||
*/
|
||||
public function subscribe($folder)
|
||||
{
|
||||
return $this->requestAndResponse('SUBSCRIBE', [$this->escapeString($folder)], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* permanently remove messages
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
public function expunge()
|
||||
{
|
||||
// TODO: parse response?
|
||||
return $this->requestAndResponse('EXPUNGE');
|
||||
}
|
||||
|
||||
/**
|
||||
* send noop
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
public function noop()
|
||||
{
|
||||
// TODO: parse response
|
||||
return $this->requestAndResponse('NOOP');
|
||||
}
|
||||
|
||||
/**
|
||||
* do a search request
|
||||
*
|
||||
* This method is currently marked as internal as the API might change and is not
|
||||
* safe if you don't take precautions.
|
||||
*
|
||||
* @param array $params
|
||||
* @return array message ids
|
||||
*/
|
||||
public function search(array $params)
|
||||
{
|
||||
$response = $this->requestAndResponse('SEARCH', $params);
|
||||
if (! $response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
foreach ($response as $ids) {
|
||||
if ($ids[0] == 'SEARCH') {
|
||||
array_shift($ids);
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
401
vendor/laminas/laminas-mail/src/Protocol/Pop3.php
vendored
Normal file
401
vendor/laminas/laminas-mail/src/Protocol/Pop3.php
vendored
Normal file
@ -0,0 +1,401 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol;
|
||||
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
|
||||
class Pop3
|
||||
{
|
||||
use ProtocolTrait;
|
||||
|
||||
/**
|
||||
* Default timeout in seconds for initiating session
|
||||
*/
|
||||
const TIMEOUT_CONNECTION = 30;
|
||||
|
||||
/**
|
||||
* saves if server supports top
|
||||
* @var null|bool
|
||||
*/
|
||||
public $hasTop = null;
|
||||
|
||||
/**
|
||||
* @var null|resource
|
||||
*/
|
||||
protected $socket;
|
||||
|
||||
/**
|
||||
* greeting timestamp for apop
|
||||
* @var null|string
|
||||
*/
|
||||
protected $timestamp;
|
||||
|
||||
/**
|
||||
* Public constructor
|
||||
*
|
||||
* @param string $host hostname or IP address of POP3 server, if given connect() is called
|
||||
* @param int|null $port port of POP3 server, null for default (110 or 995 for ssl)
|
||||
* @param bool|string $ssl use ssl? 'SSL', 'TLS' or false
|
||||
* @param bool $novalidatecert set to true to skip SSL certificate validation
|
||||
*/
|
||||
public function __construct($host = '', $port = null, $ssl = false, $novalidatecert = false)
|
||||
{
|
||||
$this->setNoValidateCert($novalidatecert);
|
||||
|
||||
if ($host) {
|
||||
$this->connect($host, $port, $ssl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open connection to POP3 server
|
||||
*
|
||||
* @param string $host hostname or IP address of POP3 server
|
||||
* @param int|null $port of POP3 server, default is 110 (995 for ssl)
|
||||
* @param string|bool $ssl use 'SSL', 'TLS' or false
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string welcome message
|
||||
*/
|
||||
public function connect($host, $port = null, $ssl = false)
|
||||
{
|
||||
$transport = 'tcp';
|
||||
$isTls = false;
|
||||
|
||||
if ($ssl) {
|
||||
$ssl = strtolower($ssl);
|
||||
}
|
||||
|
||||
switch ($ssl) {
|
||||
case 'ssl':
|
||||
$transport = 'ssl';
|
||||
if (! $port) {
|
||||
$port = 995;
|
||||
}
|
||||
break;
|
||||
case 'tls':
|
||||
$isTls = true;
|
||||
// break intentionally omitted
|
||||
default:
|
||||
if (! $port) {
|
||||
$port = 110;
|
||||
}
|
||||
}
|
||||
|
||||
$this->socket = $this->setupSocket($transport, $host, $port, self::TIMEOUT_CONNECTION);
|
||||
|
||||
$welcome = $this->readResponse();
|
||||
|
||||
strtok($welcome, '<');
|
||||
$this->timestamp = strtok('>');
|
||||
if (! strpos($this->timestamp, '@')) {
|
||||
$this->timestamp = null;
|
||||
} else {
|
||||
$this->timestamp = '<' . $this->timestamp . '>';
|
||||
}
|
||||
|
||||
if ($isTls) {
|
||||
$this->request('STLS');
|
||||
$result = stream_socket_enable_crypto($this->socket, true, $this->getCryptoMethod());
|
||||
if (! $result) {
|
||||
throw new Exception\RuntimeException('cannot enable TLS');
|
||||
}
|
||||
}
|
||||
|
||||
return $welcome;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request
|
||||
*
|
||||
* @param string $request your request without newline
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function sendRequest($request)
|
||||
{
|
||||
ErrorHandler::start();
|
||||
$result = fputs($this->socket, $request . "\r\n");
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $result) {
|
||||
throw new Exception\RuntimeException('send failed - connection closed?', 0, $error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read a response
|
||||
*
|
||||
* @param bool $multiline response has multiple lines and should be read until "<nl>.<nl>"
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string response
|
||||
*/
|
||||
public function readResponse($multiline = false)
|
||||
{
|
||||
ErrorHandler::start();
|
||||
$result = fgets($this->socket);
|
||||
$error = ErrorHandler::stop();
|
||||
if (! is_string($result)) {
|
||||
throw new Exception\RuntimeException('read failed - connection closed?', 0, $error);
|
||||
}
|
||||
|
||||
$result = trim($result);
|
||||
if (strpos($result, ' ')) {
|
||||
list($status, $message) = explode(' ', $result, 2);
|
||||
} else {
|
||||
$status = $result;
|
||||
$message = '';
|
||||
}
|
||||
|
||||
if ($status != '+OK') {
|
||||
throw new Exception\RuntimeException('last request failed');
|
||||
}
|
||||
|
||||
if ($multiline) {
|
||||
$message = '';
|
||||
$line = fgets($this->socket);
|
||||
while ($line && rtrim($line, "\r\n") != '.') {
|
||||
if ($line[0] == '.') {
|
||||
$line = substr($line, 1);
|
||||
}
|
||||
$message .= $line;
|
||||
$line = fgets($this->socket);
|
||||
};
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request and get response
|
||||
*
|
||||
* @see sendRequest()
|
||||
* @see readResponse()
|
||||
* @param string $request request
|
||||
* @param bool $multiline multiline response?
|
||||
* @return string result from readResponse()
|
||||
*/
|
||||
public function request($request, $multiline = false)
|
||||
{
|
||||
$this->sendRequest($request);
|
||||
return $this->readResponse($multiline);
|
||||
}
|
||||
|
||||
/**
|
||||
* End communication with POP3 server (also closes socket)
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
if ($this->socket) {
|
||||
try {
|
||||
$this->request('QUIT');
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
// ignore error - we're closing the socket anyway
|
||||
}
|
||||
|
||||
fclose($this->socket);
|
||||
$this->socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get capabilities from POP3 server
|
||||
*
|
||||
* @return array list of capabilities
|
||||
*/
|
||||
public function capa()
|
||||
{
|
||||
$result = $this->request('CAPA', true);
|
||||
return explode("\n", $result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login to POP3 server. Can use APOP
|
||||
*
|
||||
* @param string $user username
|
||||
* @param string $password password
|
||||
* @param bool $tryApop should APOP be tried?
|
||||
*/
|
||||
public function login($user, $password, $tryApop = true)
|
||||
{
|
||||
if ($tryApop && $this->timestamp) {
|
||||
try {
|
||||
$this->request("APOP $user " . md5($this->timestamp . $password));
|
||||
return;
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
$this->request("USER $user");
|
||||
$this->request("PASS $password");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make STAT call for message count and size sum
|
||||
*
|
||||
* @param int $messages out parameter with count of messages
|
||||
* @param int $octets out parameter with size in octets of messages
|
||||
*/
|
||||
public function status(&$messages, &$octets)
|
||||
{
|
||||
$messages = 0;
|
||||
$octets = 0;
|
||||
$result = $this->request('STAT');
|
||||
|
||||
list($messages, $octets) = explode(' ', $result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make LIST call for size of message(s)
|
||||
*
|
||||
* @param int|null $msgno number of message, null for all
|
||||
* @return int|array size of given message or list with array(num => size)
|
||||
*/
|
||||
public function getList($msgno = null)
|
||||
{
|
||||
if ($msgno !== null) {
|
||||
$result = $this->request("LIST $msgno");
|
||||
|
||||
list(, $result) = explode(' ', $result);
|
||||
return (int) $result;
|
||||
}
|
||||
|
||||
$result = $this->request('LIST', true);
|
||||
$messages = [];
|
||||
$line = strtok($result, "\n");
|
||||
while ($line) {
|
||||
list($no, $size) = explode(' ', trim($line));
|
||||
$messages[(int) $no] = (int) $size;
|
||||
$line = strtok("\n");
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make UIDL call for getting a uniqueid
|
||||
*
|
||||
* @param int|null $msgno number of message, null for all
|
||||
* @return string|array uniqueid of message or list with array(num => uniqueid)
|
||||
*/
|
||||
public function uniqueid($msgno = null)
|
||||
{
|
||||
if ($msgno !== null) {
|
||||
$result = $this->request("UIDL $msgno");
|
||||
|
||||
list(, $result) = explode(' ', $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->request('UIDL', true);
|
||||
|
||||
$result = explode("\n", $result);
|
||||
$messages = [];
|
||||
foreach ($result as $line) {
|
||||
if (! $line) {
|
||||
continue;
|
||||
}
|
||||
list($no, $id) = explode(' ', trim($line), 2);
|
||||
$messages[(int) $no] = $id;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make TOP call for getting headers and maybe some body lines
|
||||
* This method also sets hasTop - before it it's not known if top is supported
|
||||
*
|
||||
* The fallback makes normal RETR call, which retrieves the whole message. Additional
|
||||
* lines are not removed.
|
||||
*
|
||||
* @param int $msgno number of message
|
||||
* @param int $lines number of wanted body lines (empty line is inserted after header lines)
|
||||
* @param bool $fallback fallback with full retrieve if top is not supported
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Exception\ExceptionInterface
|
||||
* @return string message headers with wanted body lines
|
||||
*/
|
||||
public function top($msgno, $lines = 0, $fallback = false)
|
||||
{
|
||||
if ($this->hasTop === false) {
|
||||
if ($fallback) {
|
||||
return $this->retrieve($msgno);
|
||||
} else {
|
||||
throw new Exception\RuntimeException('top not supported and no fallback wanted');
|
||||
}
|
||||
}
|
||||
$this->hasTop = true;
|
||||
|
||||
$lines = (! $lines || $lines < 1) ? 0 : (int) $lines;
|
||||
|
||||
try {
|
||||
$result = $this->request("TOP $msgno $lines", true);
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
$this->hasTop = false;
|
||||
if ($fallback) {
|
||||
$result = $this->retrieve($msgno);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a RETR call for retrieving a full message with headers and body
|
||||
*
|
||||
* @param int $msgno message number
|
||||
* @return string message
|
||||
*/
|
||||
public function retrieve($msgno)
|
||||
{
|
||||
$result = $this->request("RETR $msgno", true);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a NOOP call, maybe needed for keeping the server happy
|
||||
*/
|
||||
public function noop()
|
||||
{
|
||||
$this->request('NOOP');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a DELE count to remove a message
|
||||
*
|
||||
* @param $msgno
|
||||
*/
|
||||
public function delete($msgno)
|
||||
{
|
||||
$this->request("DELE $msgno");
|
||||
}
|
||||
|
||||
/**
|
||||
* Make RSET call, which rollbacks delete requests
|
||||
*/
|
||||
public function undelete()
|
||||
{
|
||||
$this->request('RSET');
|
||||
}
|
||||
}
|
||||
119
vendor/laminas/laminas-mail/src/Protocol/ProtocolTrait.php
vendored
Normal file
119
vendor/laminas/laminas-mail/src/Protocol/ProtocolTrait.php
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol;
|
||||
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
|
||||
/**
|
||||
* https://bugs.php.net/bug.php?id=69195
|
||||
*/
|
||||
trait ProtocolTrait
|
||||
{
|
||||
/**
|
||||
* If set to true, do not validate the SSL certificate
|
||||
* @var null|bool
|
||||
*/
|
||||
protected $novalidatecert;
|
||||
|
||||
|
||||
public function getCryptoMethod(): int
|
||||
{
|
||||
// Allow the best TLS version(s) we can
|
||||
$cryptoMethod = STREAM_CRYPTO_METHOD_TLS_CLIENT;
|
||||
|
||||
// PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
|
||||
// so add them back in manually if we can
|
||||
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
|
||||
$cryptoMethod |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
|
||||
$cryptoMethod |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
|
||||
}
|
||||
|
||||
return $cryptoMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not validate SSL certificate
|
||||
*
|
||||
* @todo Update to return self when minimum supported PHP version is 7.4+
|
||||
* @param bool $novalidatecert Set to true to disable certificate validation
|
||||
* @return $this
|
||||
*/
|
||||
public function setNoValidateCert(bool $novalidatecert)
|
||||
{
|
||||
$this->novalidatecert = $novalidatecert;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we validate SSL certificate?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateCert(): bool
|
||||
{
|
||||
return ! $this->novalidatecert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare socket options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function prepareSocketOptions(): array
|
||||
{
|
||||
return $this->novalidatecert
|
||||
? [
|
||||
'ssl' => [
|
||||
'verify_peer_name' => false,
|
||||
'verify_peer' => false,
|
||||
]
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup connection socket
|
||||
*
|
||||
* @param string $host hostname or IP address of IMAP server
|
||||
* @param int|null $port of IMAP server, default is 143 (993 for ssl)
|
||||
* @param int $timeout timeout in seconds for initiating session
|
||||
* @return resource The socket created.
|
||||
* @throws Exception\RuntimeException If unable to connect to host.
|
||||
*/
|
||||
protected function setupSocket(
|
||||
string $transport,
|
||||
string $host,
|
||||
?int $port,
|
||||
int $timeout
|
||||
) {
|
||||
ErrorHandler::start();
|
||||
$socket = stream_socket_client(
|
||||
sprintf('%s://%s:%d', $transport, $host, $port),
|
||||
$errno,
|
||||
$errstr,
|
||||
$timeout,
|
||||
STREAM_CLIENT_CONNECT,
|
||||
stream_context_create($this->prepareSocketOptions())
|
||||
);
|
||||
$error = ErrorHandler::stop();
|
||||
|
||||
if (! $socket) {
|
||||
throw new Exception\RuntimeException(sprintf(
|
||||
'cannot connect to host%s',
|
||||
$error ? sprintf('; error = %s (errno = %d )', $error->getMessage(), $error->getCode()) : ''
|
||||
), 0, $error);
|
||||
}
|
||||
|
||||
if (false === stream_set_timeout($socket, $timeout)) {
|
||||
throw new Exception\RuntimeException('Could not set stream timeout');
|
||||
}
|
||||
|
||||
return $socket;
|
||||
}
|
||||
}
|
||||
460
vendor/laminas/laminas-mail/src/Protocol/Smtp.php
vendored
Normal file
460
vendor/laminas/laminas-mail/src/Protocol/Smtp.php
vendored
Normal file
@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol;
|
||||
|
||||
/**
|
||||
* SMTP implementation of Laminas\Mail\Protocol\AbstractProtocol
|
||||
*
|
||||
* Minimum implementation according to RFC2821: EHLO, MAIL FROM, RCPT TO, DATA,
|
||||
* RSET, NOOP, QUIT
|
||||
*/
|
||||
class Smtp extends AbstractProtocol
|
||||
{
|
||||
use ProtocolTrait;
|
||||
|
||||
/**
|
||||
* The transport method for the socket
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $transport = 'tcp';
|
||||
|
||||
/**
|
||||
* Indicates that a session is requested to be secure
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $secure;
|
||||
|
||||
/**
|
||||
* Indicates an smtp session has been started by the HELO command
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $sess = false;
|
||||
|
||||
/**
|
||||
* Indicates an smtp AUTH has been issued and authenticated
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $auth = false;
|
||||
|
||||
/**
|
||||
* Indicates a MAIL command has been issued
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $mail = false;
|
||||
|
||||
/**
|
||||
* Indicates one or more RCTP commands have been issued
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $rcpt = false;
|
||||
|
||||
/**
|
||||
* Indicates that DATA has been issued and sent
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $data = null;
|
||||
|
||||
/**
|
||||
* Whether or not send QUIT command
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $useCompleteQuit = true;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* The first argument may be an array of all options. If so, it must include
|
||||
* the 'host' and 'port' keys in order to ensure that all required values
|
||||
* are present.
|
||||
*
|
||||
* @param string|array $host
|
||||
* @param null|int $port
|
||||
* @param null|array $config
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($host = '127.0.0.1', $port = null, array $config = null)
|
||||
{
|
||||
// Did we receive a configuration array?
|
||||
if (is_array($host)) {
|
||||
// Merge config array with principal array, if provided
|
||||
if (is_array($config)) {
|
||||
$config = array_replace_recursive($host, $config);
|
||||
} else {
|
||||
$config = $host;
|
||||
}
|
||||
|
||||
// Look for a host key; if none found, use default value
|
||||
if (isset($config['host'])) {
|
||||
$host = $config['host'];
|
||||
} else {
|
||||
$host = '127.0.0.1';
|
||||
}
|
||||
|
||||
// Look for a port key; if none found, use default value
|
||||
if (isset($config['port'])) {
|
||||
$port = $config['port'];
|
||||
} else {
|
||||
$port = null;
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a config array, initialize it
|
||||
if (null === $config) {
|
||||
$config = [];
|
||||
}
|
||||
|
||||
if (isset($config['ssl'])) {
|
||||
switch (strtolower($config['ssl'])) {
|
||||
case 'tls':
|
||||
$this->secure = 'tls';
|
||||
break;
|
||||
|
||||
case 'ssl':
|
||||
$this->transport = 'ssl';
|
||||
$this->secure = 'ssl';
|
||||
if ($port === null) {
|
||||
$port = 465;
|
||||
}
|
||||
break;
|
||||
|
||||
case '':
|
||||
// fall-through
|
||||
case 'none':
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception\InvalidArgumentException($config['ssl'] . ' is unsupported SSL type');
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('use_complete_quit', $config)) {
|
||||
$this->setUseCompleteQuit($config['use_complete_quit']);
|
||||
}
|
||||
|
||||
// If no port has been specified then check the master PHP ini file. Defaults to 25 if the ini setting is null.
|
||||
if ($port === null) {
|
||||
if (($port = ini_get('smtp_port')) == '') {
|
||||
$port = 25;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('novalidatecert', $config)) {
|
||||
$this->setNoValidateCert($config['novalidatecert']);
|
||||
}
|
||||
|
||||
parent::__construct($host, $port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not send QUIT command
|
||||
*
|
||||
* @param bool $useCompleteQuit use complete quit
|
||||
* @return bool
|
||||
*/
|
||||
public function setUseCompleteQuit($useCompleteQuit)
|
||||
{
|
||||
return $this->useCompleteQuit = (bool) $useCompleteQuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not send QUIT command
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function useCompleteQuit()
|
||||
{
|
||||
return $this->useCompleteQuit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the server with the parameters given in the constructor.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
$this->socket = $this->setupSocket(
|
||||
$this->transport,
|
||||
$this->host,
|
||||
$this->port,
|
||||
self::TIMEOUT_CONNECTION
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate HELO/EHLO sequence and set flag to indicate valid smtp session
|
||||
*
|
||||
* @param string $host The client hostname or IP address (default: 127.0.0.1)
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function helo($host = '127.0.0.1')
|
||||
{
|
||||
// Respect RFC 2821 and disallow HELO attempts if session is already initiated.
|
||||
if ($this->sess === true) {
|
||||
throw new Exception\RuntimeException('Cannot issue HELO to existing session');
|
||||
}
|
||||
|
||||
// Validate client hostname
|
||||
if (! $this->validHost->isValid($host)) {
|
||||
throw new Exception\RuntimeException(implode(', ', $this->validHost->getMessages()));
|
||||
}
|
||||
|
||||
// Initiate helo sequence
|
||||
$this->_expect(220, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
$this->ehlo($host);
|
||||
|
||||
// If a TLS session is required, commence negotiation
|
||||
if ($this->secure == 'tls') {
|
||||
$this->_send('STARTTLS');
|
||||
$this->_expect(220, 180);
|
||||
if (! stream_socket_enable_crypto($this->socket, true, $this->getCryptoMethod())) {
|
||||
throw new Exception\RuntimeException('Unable to connect via TLS');
|
||||
}
|
||||
$this->ehlo($host);
|
||||
}
|
||||
|
||||
$this->startSession();
|
||||
$this->auth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the perceived session status
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSession()
|
||||
{
|
||||
return $this->sess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send EHLO or HELO depending on capabilities of smtp host
|
||||
*
|
||||
* @param string $host The client hostname or IP address (default: 127.0.0.1)
|
||||
* @throws \Exception|Exception\ExceptionInterface
|
||||
*/
|
||||
protected function ehlo($host)
|
||||
{
|
||||
// Support for older, less-compliant remote servers. Tries multiple attempts of EHLO or HELO.
|
||||
try {
|
||||
$this->_send('EHLO ' . $host);
|
||||
$this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
$this->_send('HELO ' . $host);
|
||||
$this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Issues MAIL command
|
||||
*
|
||||
* @param string $from Sender mailbox
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function mail($from)
|
||||
{
|
||||
if ($this->sess !== true) {
|
||||
throw new Exception\RuntimeException('A valid session has not been started');
|
||||
}
|
||||
|
||||
$this->_send('MAIL FROM:<' . $from . '>');
|
||||
$this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
|
||||
// Set mail to true, clear recipients and any existing data flags as per 4.1.1.2 of RFC 2821
|
||||
$this->mail = true;
|
||||
$this->rcpt = false;
|
||||
$this->data = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Issues RCPT command
|
||||
*
|
||||
* @param string $to Receiver(s) mailbox
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function rcpt($to)
|
||||
{
|
||||
if ($this->mail !== true) {
|
||||
throw new Exception\RuntimeException('No sender reverse path has been supplied');
|
||||
}
|
||||
|
||||
// Set rcpt to true, as per 4.1.1.3 of RFC 2821
|
||||
$this->_send('RCPT TO:<' . $to . '>');
|
||||
$this->_expect([250, 251], 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
$this->rcpt = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Issues DATA command
|
||||
*
|
||||
* @param string $data
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function data($data)
|
||||
{
|
||||
// Ensure recipients have been set
|
||||
if ($this->rcpt !== true) { // Per RFC 2821 3.3 (page 18)
|
||||
throw new Exception\RuntimeException('No recipient forward path has been supplied');
|
||||
}
|
||||
|
||||
$this->_send('DATA');
|
||||
$this->_expect(354, 120); // Timeout set for 2 minutes as per RFC 2821 4.5.3.2
|
||||
|
||||
if (($fp = fopen("php://temp", "r+")) === false) {
|
||||
throw new Exception\RuntimeException('cannot fopen');
|
||||
}
|
||||
if (fwrite($fp, $data) === false) {
|
||||
throw new Exception\RuntimeException('cannot fwrite');
|
||||
}
|
||||
unset($data);
|
||||
rewind($fp);
|
||||
|
||||
// max line length is 998 char + \r\n = 1000
|
||||
while (($line = stream_get_line($fp, 1000, "\n")) !== false) {
|
||||
$line = rtrim($line, "\r");
|
||||
if (isset($line[0]) && $line[0] === '.') {
|
||||
// Escape lines prefixed with a '.'
|
||||
$line = '.' . $line;
|
||||
}
|
||||
$this->_send($line);
|
||||
}
|
||||
fclose($fp);
|
||||
|
||||
$this->_send('.');
|
||||
$this->_expect(250, 600); // Timeout set for 10 minutes as per RFC 2821 4.5.3.2
|
||||
$this->data = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Issues the RSET command end validates answer
|
||||
*
|
||||
* Can be used to restore a clean smtp communication state when a
|
||||
* transaction has been cancelled or commencing a new transaction.
|
||||
*/
|
||||
public function rset()
|
||||
{
|
||||
$this->_send('RSET');
|
||||
// MS ESMTP doesn't follow RFC, see https://zendframework.com/issues/browse/ZF-1377
|
||||
$this->_expect([250, 220]);
|
||||
|
||||
$this->mail = false;
|
||||
$this->rcpt = false;
|
||||
$this->data = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues the NOOP command end validates answer
|
||||
*
|
||||
* Not used by Laminas\Mail, could be used to keep a connection alive or check if it is still open.
|
||||
*
|
||||
*/
|
||||
public function noop()
|
||||
{
|
||||
$this->_send('NOOP');
|
||||
$this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues the VRFY command end validates answer
|
||||
*
|
||||
* Not used by Laminas\Mail.
|
||||
*
|
||||
* @param string $user User Name or eMail to verify
|
||||
*/
|
||||
public function vrfy($user)
|
||||
{
|
||||
$this->_send('VRFY ' . $user);
|
||||
$this->_expect([250, 251, 252], 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues the QUIT command and clears the current session
|
||||
*
|
||||
*/
|
||||
public function quit()
|
||||
{
|
||||
if ($this->sess) {
|
||||
$this->auth = false;
|
||||
|
||||
if ($this->useCompleteQuit()) {
|
||||
$this->_send('QUIT');
|
||||
$this->_expect(221, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2
|
||||
}
|
||||
|
||||
$this->stopSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default authentication method
|
||||
*
|
||||
* This default method is implemented by AUTH adapters to properly authenticate to a remote host.
|
||||
*
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
if ($this->auth === true) {
|
||||
throw new Exception\RuntimeException('Already authenticated for this session');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes connection
|
||||
*
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$this->_disconnect();
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* Disconnect from remote host and free resource
|
||||
*/
|
||||
protected function _disconnect()
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
// Make sure the session gets closed
|
||||
$this->quit();
|
||||
parent::_disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start mail session
|
||||
*
|
||||
*/
|
||||
protected function startSession()
|
||||
{
|
||||
$this->sess = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop mail session
|
||||
*
|
||||
*/
|
||||
protected function stopSession()
|
||||
{
|
||||
$this->sess = false;
|
||||
}
|
||||
}
|
||||
138
vendor/laminas/laminas-mail/src/Protocol/Smtp/Auth/Crammd5.php
vendored
Normal file
138
vendor/laminas/laminas-mail/src/Protocol/Smtp/Auth/Crammd5.php
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol\Smtp\Auth;
|
||||
|
||||
use Laminas\Crypt\Hmac;
|
||||
use Laminas\Mail\Protocol\Smtp;
|
||||
|
||||
/**
|
||||
* Performs CRAM-MD5 authentication
|
||||
*/
|
||||
class Crammd5 extends Smtp
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* All parameters may be passed as an array to the first argument of the
|
||||
* constructor. If so,
|
||||
*
|
||||
* @param string|array $host (Default: 127.0.0.1)
|
||||
* @param null|int $port (Default: null)
|
||||
* @param null|array $config Auth-specific parameters
|
||||
*/
|
||||
public function __construct($host = '127.0.0.1', $port = null, $config = null)
|
||||
{
|
||||
// Did we receive a configuration array?
|
||||
$origConfig = $config;
|
||||
if (is_array($host)) {
|
||||
// Merge config array with principal array, if provided
|
||||
if (is_array($config)) {
|
||||
$config = array_replace_recursive($host, $config);
|
||||
} else {
|
||||
$config = $host;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($config)) {
|
||||
if (isset($config['username'])) {
|
||||
$this->setUsername($config['username']);
|
||||
}
|
||||
if (isset($config['password'])) {
|
||||
$this->setPassword($config['password']);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent with original arguments
|
||||
parent::__construct($host, $port, $origConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs CRAM-MD5 authentication with supplied credentials
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
// Ensure AUTH has not already been initiated.
|
||||
parent::auth();
|
||||
|
||||
$this->_send('AUTH CRAM-MD5');
|
||||
$challenge = $this->_expect(334);
|
||||
$challenge = base64_decode($challenge);
|
||||
$digest = $this->hmacMd5($this->getPassword(), $challenge);
|
||||
$this->_send(base64_encode($this->getUsername() . ' ' . $digest));
|
||||
$this->_expect(235);
|
||||
$this->auth = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value for username
|
||||
*
|
||||
* @param string $username
|
||||
* @return Crammd5
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value for password
|
||||
*
|
||||
* @param string $password
|
||||
* @return Crammd5
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare CRAM-MD5 response to server's ticket
|
||||
*
|
||||
* @param string $key Challenge key (usually password)
|
||||
* @param string $data Challenge data
|
||||
* @param int $block Length of blocks (deprecated; unused)
|
||||
* @return string
|
||||
*/
|
||||
protected function hmacMd5($key, $data, $block = 64)
|
||||
{
|
||||
return Hmac::compute($key, 'md5', $data);
|
||||
}
|
||||
}
|
||||
126
vendor/laminas/laminas-mail/src/Protocol/Smtp/Auth/Login.php
vendored
Normal file
126
vendor/laminas/laminas-mail/src/Protocol/Smtp/Auth/Login.php
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol\Smtp\Auth;
|
||||
|
||||
use Laminas\Mail\Protocol\Smtp;
|
||||
|
||||
/**
|
||||
* Performs LOGIN authentication
|
||||
*/
|
||||
class Login extends Smtp
|
||||
{
|
||||
/**
|
||||
* LOGIN username
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* LOGIN password
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $host (Default: 127.0.0.1)
|
||||
* @param int $port (Default: null)
|
||||
* @param array $config Auth-specific parameters
|
||||
*/
|
||||
public function __construct($host = '127.0.0.1', $port = null, $config = null)
|
||||
{
|
||||
// Did we receive a configuration array?
|
||||
$origConfig = $config;
|
||||
if (is_array($host)) {
|
||||
// Merge config array with principal array, if provided
|
||||
if (is_array($config)) {
|
||||
$config = array_replace_recursive($host, $config);
|
||||
} else {
|
||||
$config = $host;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($config)) {
|
||||
if (isset($config['username'])) {
|
||||
$this->setUsername($config['username']);
|
||||
}
|
||||
if (isset($config['password'])) {
|
||||
$this->setPassword($config['password']);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent with original arguments
|
||||
parent::__construct($host, $port, $origConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform LOGIN authentication with supplied credentials
|
||||
*
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
// Ensure AUTH has not already been initiated.
|
||||
parent::auth();
|
||||
|
||||
$this->_send('AUTH LOGIN');
|
||||
$this->_expect(334);
|
||||
$this->_send(base64_encode($this->getUsername()));
|
||||
$this->_expect(334);
|
||||
$this->_send(base64_encode($this->getPassword()));
|
||||
$this->_expect(235);
|
||||
$this->auth = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value for username
|
||||
*
|
||||
* @param string $username
|
||||
* @return Login
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value for password
|
||||
*
|
||||
* @param string $password
|
||||
* @return Login
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
}
|
||||
124
vendor/laminas/laminas-mail/src/Protocol/Smtp/Auth/Plain.php
vendored
Normal file
124
vendor/laminas/laminas-mail/src/Protocol/Smtp/Auth/Plain.php
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol\Smtp\Auth;
|
||||
|
||||
use Laminas\Mail\Protocol\Smtp;
|
||||
|
||||
/**
|
||||
* Performs PLAIN authentication
|
||||
*/
|
||||
class Plain extends Smtp
|
||||
{
|
||||
/**
|
||||
* PLAIN username
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* PLAIN password
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $host (Default: 127.0.0.1)
|
||||
* @param int $port (Default: null)
|
||||
* @param array $config Auth-specific parameters
|
||||
*/
|
||||
public function __construct($host = '127.0.0.1', $port = null, $config = null)
|
||||
{
|
||||
// Did we receive a configuration array?
|
||||
$origConfig = $config;
|
||||
if (is_array($host)) {
|
||||
// Merge config array with principal array, if provided
|
||||
if (is_array($config)) {
|
||||
$config = array_replace_recursive($host, $config);
|
||||
} else {
|
||||
$config = $host;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($config)) {
|
||||
if (isset($config['username'])) {
|
||||
$this->setUsername($config['username']);
|
||||
}
|
||||
if (isset($config['password'])) {
|
||||
$this->setPassword($config['password']);
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent with original arguments
|
||||
parent::__construct($host, $port, $origConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform PLAIN authentication with supplied credentials
|
||||
*
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
// Ensure AUTH has not already been initiated.
|
||||
parent::auth();
|
||||
|
||||
$this->_send('AUTH PLAIN');
|
||||
$this->_expect(334);
|
||||
$this->_send(base64_encode("\0" . $this->getUsername() . "\0" . $this->getPassword()));
|
||||
$this->_expect(235);
|
||||
$this->auth = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value for username
|
||||
*
|
||||
* @param string $username
|
||||
* @return Plain
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value for password
|
||||
*
|
||||
* @param string $password
|
||||
* @return Plain
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
}
|
||||
114
vendor/laminas/laminas-mail/src/Protocol/SmtpPluginManager.php
vendored
Normal file
114
vendor/laminas/laminas-mail/src/Protocol/SmtpPluginManager.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol;
|
||||
|
||||
use Laminas\ServiceManager\AbstractPluginManager;
|
||||
use Laminas\ServiceManager\Exception\InvalidServiceException;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
|
||||
/**
|
||||
* Plugin manager implementation for SMTP extensions.
|
||||
*
|
||||
* Enforces that SMTP extensions retrieved are instances of Smtp. Additionally,
|
||||
* it registers a number of default extensions available.
|
||||
*/
|
||||
class SmtpPluginManager extends AbstractPluginManager
|
||||
{
|
||||
/**
|
||||
* Service aliases
|
||||
*/
|
||||
protected $aliases = [
|
||||
'crammd5' => Smtp\Auth\Crammd5::class,
|
||||
'cramMd5' => Smtp\Auth\Crammd5::class,
|
||||
'CramMd5' => Smtp\Auth\Crammd5::class,
|
||||
'cramMD5' => Smtp\Auth\Crammd5::class,
|
||||
'CramMD5' => Smtp\Auth\Crammd5::class,
|
||||
'login' => Smtp\Auth\Login::class,
|
||||
'Login' => Smtp\Auth\Login::class,
|
||||
'plain' => Smtp\Auth\Plain::class,
|
||||
'Plain' => Smtp\Auth\Plain::class,
|
||||
'smtp' => Smtp::class,
|
||||
'Smtp' => Smtp::class,
|
||||
'SMTP' => Smtp::class,
|
||||
|
||||
// Legacy Zend Framework aliases
|
||||
\Zend\Mail\Protocol\Smtp\Auth\Crammd5::class => Smtp\Auth\Crammd5::class,
|
||||
\Zend\Mail\Protocol\Smtp\Auth\Login::class => Smtp\Auth\Login::class,
|
||||
\Zend\Mail\Protocol\Smtp\Auth\Plain::class => Smtp\Auth\Plain::class,
|
||||
\Zend\Mail\Protocol\Smtp::class => Smtp::class,
|
||||
|
||||
// v2 normalized FQCNs
|
||||
'zendmailprotocolsmtpauthcrammd5' => Smtp\Auth\Crammd5::class,
|
||||
'zendmailprotocolsmtpauthlogin' => Smtp\Auth\Login::class,
|
||||
'zendmailprotocolsmtpauthplain' => Smtp\Auth\Plain::class,
|
||||
'zendmailprotocolsmtp' => Smtp::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Service factories
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $factories = [
|
||||
Smtp\Auth\Crammd5::class => InvokableFactory::class,
|
||||
Smtp\Auth\Login::class => InvokableFactory::class,
|
||||
Smtp\Auth\Plain::class => InvokableFactory::class,
|
||||
Smtp::class => InvokableFactory::class,
|
||||
|
||||
// v2 normalized service names
|
||||
|
||||
'laminasmailprotocolsmtpauthcrammd5' => InvokableFactory::class,
|
||||
'laminasmailprotocolsmtpauthlogin' => InvokableFactory::class,
|
||||
'laminasmailprotocolsmtpauthplain' => InvokableFactory::class,
|
||||
'laminasmailprotocolsmtp' => InvokableFactory::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Plugins must be an instance of the Smtp class
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $instanceOf = Smtp::class;
|
||||
|
||||
/**
|
||||
* Validate a retrieved plugin instance (v3).
|
||||
*
|
||||
* @param object $plugin
|
||||
* @throws InvalidServiceException
|
||||
*/
|
||||
public function validate($plugin)
|
||||
{
|
||||
if (! $plugin instanceof $this->instanceOf) {
|
||||
throw new InvalidServiceException(sprintf(
|
||||
'Plugin of type %s is invalid; must extend %s',
|
||||
(is_object($plugin) ? get_class($plugin) : gettype($plugin)),
|
||||
Smtp::class
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a retrieved plugin instance (v2).
|
||||
*
|
||||
* @param object $plugin
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function validatePlugin($plugin)
|
||||
{
|
||||
try {
|
||||
$this->validate($plugin);
|
||||
} catch (InvalidServiceException $e) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
$e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
vendor/laminas/laminas-mail/src/Protocol/SmtpPluginManagerFactory.php
vendored
Normal file
54
vendor/laminas/laminas-mail/src/Protocol/SmtpPluginManagerFactory.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Protocol;
|
||||
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Laminas\ServiceManager\FactoryInterface;
|
||||
use Laminas\ServiceManager\ServiceLocatorInterface;
|
||||
|
||||
class SmtpPluginManagerFactory implements FactoryInterface
|
||||
{
|
||||
/**
|
||||
* laminas-servicemanager v2 support for invocation options.
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
protected $creationOptions;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return SmtpPluginManager
|
||||
*/
|
||||
public function __invoke(ContainerInterface $container, $name, array $options = null)
|
||||
{
|
||||
return new SmtpPluginManager($container, $options ?: []);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return SmtpPluginManager
|
||||
*/
|
||||
public function createService(ServiceLocatorInterface $container, $name = null, $requestedName = null)
|
||||
{
|
||||
return $this($container, $requestedName ?: SmtpPluginManager::class, $this->creationOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* laminas-servicemanager v2 support for invocation options.
|
||||
*
|
||||
* @param array $options
|
||||
* @return void
|
||||
*/
|
||||
public function setCreationOptions(array $options)
|
||||
{
|
||||
$this->creationOptions = $options;
|
||||
}
|
||||
}
|
||||
23
vendor/laminas/laminas-mail/src/Storage.php
vendored
Normal file
23
vendor/laminas/laminas-mail/src/Storage.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail;
|
||||
|
||||
class Storage
|
||||
{
|
||||
// maildir and IMAP flags, using IMAP names, where possible to be able to distinguish between IMAP
|
||||
// system flags and other flags
|
||||
const FLAG_PASSED = 'Passed';
|
||||
const FLAG_SEEN = '\Seen';
|
||||
const FLAG_UNSEEN = '\Unseen';
|
||||
const FLAG_ANSWERED = '\Answered';
|
||||
const FLAG_FLAGGED = '\Flagged';
|
||||
const FLAG_DELETED = '\Deleted';
|
||||
const FLAG_DRAFT = '\Draft';
|
||||
const FLAG_RECENT = '\Recent';
|
||||
}
|
||||
319
vendor/laminas/laminas-mail/src/Storage/AbstractStorage.php
vendored
Normal file
319
vendor/laminas/laminas-mail/src/Storage/AbstractStorage.php
vendored
Normal file
@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use SeekableIterator;
|
||||
|
||||
abstract class AbstractStorage implements
|
||||
ArrayAccess,
|
||||
Countable,
|
||||
SeekableIterator
|
||||
{
|
||||
/**
|
||||
* class capabilities with default values
|
||||
* @var array
|
||||
*/
|
||||
protected $has = [
|
||||
'uniqueid' => true,
|
||||
'delete' => false,
|
||||
'create' => false,
|
||||
'top' => false,
|
||||
'fetchPart' => true,
|
||||
'flags' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* current iteration position
|
||||
* @var int
|
||||
*/
|
||||
protected $iterationPos = 0;
|
||||
|
||||
/**
|
||||
* maximum iteration position (= message count)
|
||||
* @var null|int
|
||||
*/
|
||||
protected $iterationMax = null;
|
||||
|
||||
/**
|
||||
* used message class, change it in an extended class to extend the returned message class
|
||||
* @var string
|
||||
*/
|
||||
protected $messageClass = Message::class;
|
||||
|
||||
/**
|
||||
* Getter for has-properties. The standard has properties
|
||||
* are: hasFolder, hasUniqueid, hasDelete, hasCreate, hasTop
|
||||
*
|
||||
* The valid values for the has-properties are:
|
||||
* - true if a feature is supported
|
||||
* - false if a feature is not supported
|
||||
* - null is it's not yet known or it can't be know if a feature is supported
|
||||
*
|
||||
* @param string $var property name
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return bool supported or not
|
||||
*/
|
||||
public function __get($var)
|
||||
{
|
||||
if (strpos($var, 'has') === 0) {
|
||||
$var = strtolower(substr($var, 3));
|
||||
return isset($this->has[$var]) ? $this->has[$var] : null;
|
||||
}
|
||||
|
||||
throw new Exception\InvalidArgumentException($var . ' not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full list of features supported by the specific mail lib and the server
|
||||
*
|
||||
* @return array list of features as array(feature_name => true|false[|null])
|
||||
*/
|
||||
public function getCapabilities()
|
||||
{
|
||||
return $this->has;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count messages messages in current box/folder
|
||||
*
|
||||
* @return int number of messages
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
abstract public function countMessages();
|
||||
|
||||
/**
|
||||
* Get a list of messages with number and size
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @return int|array size of given message of list with all messages as array(num => size)
|
||||
*/
|
||||
abstract public function getSize($id = 0);
|
||||
|
||||
/**
|
||||
* Get a message with headers and body
|
||||
*
|
||||
* @param $id int number of message
|
||||
* @return Message
|
||||
*/
|
||||
abstract public function getMessage($id);
|
||||
|
||||
/**
|
||||
* Get raw header of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message header
|
||||
* @param int $topLines include this many lines with header (after an empty line)
|
||||
* @return string raw header
|
||||
*/
|
||||
abstract public function getRawHeader($id, $part = null, $topLines = 0);
|
||||
|
||||
/**
|
||||
* Get raw content of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message content
|
||||
* @return string raw content
|
||||
*/
|
||||
abstract public function getRawContent($id, $part = null);
|
||||
|
||||
/**
|
||||
* Create instance with parameters
|
||||
*
|
||||
* @param array $params mail reader specific parameters
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
abstract public function __construct($params);
|
||||
|
||||
/**
|
||||
* Destructor calls close() and therefore closes the resource.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close resource for mail lib. If you need to control, when the resource
|
||||
* is closed. Otherwise the destructor would call this.
|
||||
*/
|
||||
abstract public function close();
|
||||
|
||||
/**
|
||||
* Keep the resource alive.
|
||||
*/
|
||||
abstract public function noop();
|
||||
|
||||
/**
|
||||
* delete a message from current box/folder
|
||||
*
|
||||
* @param $id
|
||||
*/
|
||||
abstract public function removeMessage($id);
|
||||
|
||||
/**
|
||||
* get unique id for one or all messages
|
||||
*
|
||||
* if storage does not support unique ids it's the same as the message number
|
||||
*
|
||||
* @param int|null $id message number
|
||||
* @return array|string message number for given message or all messages as array
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
abstract public function getUniqueId($id = null);
|
||||
|
||||
/**
|
||||
* get a message number from a unique id
|
||||
*
|
||||
* I.e. if you have a webmailer that supports deleting messages you should use unique ids
|
||||
* as parameter and use this method to translate it to message number right before calling removeMessage()
|
||||
*
|
||||
* @param string $id unique id
|
||||
* @return int message number
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
abstract public function getNumberByUniqueId($id);
|
||||
|
||||
// interface implementations follows
|
||||
|
||||
/**
|
||||
* Countable::count()
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->countMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayAccess::offsetExists()
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($id)
|
||||
{
|
||||
try {
|
||||
if ($this->getMessage($id)) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayAccess::offsetGet()
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Laminas\Mail\Storage\Message message object
|
||||
*/
|
||||
public function offsetGet($id)
|
||||
{
|
||||
return $this->getMessage($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayAccess::offsetSet()
|
||||
*
|
||||
* @param mixed $id
|
||||
* @param mixed $value
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function offsetSet($id, $value)
|
||||
{
|
||||
throw new Exception\RuntimeException('cannot write mail messages via array access');
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayAccess::offsetUnset()
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool success
|
||||
*/
|
||||
public function offsetUnset($id)
|
||||
{
|
||||
return $this->removeMessage($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator::rewind()
|
||||
*
|
||||
* Rewind always gets the new count from the storage. Thus if you use
|
||||
* the interfaces and your scripts take long you should use reset()
|
||||
* from time to time.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->iterationMax = $this->countMessages();
|
||||
$this->iterationPos = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator::current()
|
||||
*
|
||||
* @return Message current message
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->getMessage($this->iterationPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator::key()
|
||||
*
|
||||
* @return int id of current position
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->iterationPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator::next()
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
++$this->iterationPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator::valid()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
if ($this->iterationMax === null) {
|
||||
$this->iterationMax = $this->countMessages();
|
||||
}
|
||||
return $this->iterationPos && $this->iterationPos <= $this->iterationMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* SeekableIterator::seek()
|
||||
*
|
||||
* @param int $pos
|
||||
* @throws Exception\OutOfBoundsException
|
||||
*/
|
||||
public function seek($pos)
|
||||
{
|
||||
if ($this->iterationMax === null) {
|
||||
$this->iterationMax = $this->countMessages();
|
||||
}
|
||||
|
||||
if ($pos > $this->iterationMax) {
|
||||
throw new Exception\OutOfBoundsException('this position does not exist');
|
||||
}
|
||||
$this->iterationPos = $pos;
|
||||
}
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Storage/Exception/ExceptionInterface.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Storage/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Exception;
|
||||
|
||||
use Laminas\Mail\Exception\ExceptionInterface as MailException;
|
||||
|
||||
interface ExceptionInterface extends MailException
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Storage/Exception/InvalidArgumentException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Storage/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class InvalidArgumentException extends Exception\InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Storage/Exception/OutOfBoundsException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Storage/Exception/OutOfBoundsException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class OutOfBoundsException extends Exception\OutOfBoundsException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Storage/Exception/RuntimeException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Storage/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class RuntimeException extends Exception\RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
209
vendor/laminas/laminas-mail/src/Storage/Folder.php
vendored
Normal file
209
vendor/laminas/laminas-mail/src/Storage/Folder.php
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use RecursiveIterator;
|
||||
|
||||
class Folder implements RecursiveIterator
|
||||
{
|
||||
/**
|
||||
* subfolders of folder array(localName => \Laminas\Mail\Storage\Folder folder)
|
||||
* @var array
|
||||
*/
|
||||
protected $folders;
|
||||
|
||||
/**
|
||||
* local name (name of folder in parent folder)
|
||||
* @var string
|
||||
*/
|
||||
protected $localName;
|
||||
|
||||
/**
|
||||
* global name (absolute name of folder)
|
||||
* @var string
|
||||
*/
|
||||
protected $globalName;
|
||||
|
||||
/**
|
||||
* folder is selectable if folder is able to hold messages, otherwise it is a parent folder
|
||||
* @var bool
|
||||
*/
|
||||
protected $selectable = true;
|
||||
|
||||
/**
|
||||
* create a new mail folder instance
|
||||
*
|
||||
* @param string $localName name of folder in current subdirectory
|
||||
* @param string $globalName absolute name of folder
|
||||
* @param bool $selectable if true folder holds messages, if false it's
|
||||
* just a parent for subfolders (Default: true)
|
||||
* @param array $folders init with given instances of Folder as subfolders
|
||||
*/
|
||||
public function __construct($localName, $globalName = '', $selectable = true, array $folders = [])
|
||||
{
|
||||
$this->localName = $localName;
|
||||
$this->globalName = $globalName ? $globalName : $localName;
|
||||
$this->selectable = $selectable;
|
||||
$this->folders = $folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* implements RecursiveIterator::hasChildren()
|
||||
*
|
||||
* @return bool current element has children
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
$current = $this->current();
|
||||
return $current && $current instanceof Folder && ! $current->isLeaf();
|
||||
}
|
||||
|
||||
/**
|
||||
* implements RecursiveIterator::getChildren()
|
||||
*
|
||||
* @return \Laminas\Mail\Storage\Folder same as self::current()
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::valid()
|
||||
*
|
||||
* @return bool check if there's a current element
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return key($this->folders) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::next()
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
next($this->folders);
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::key()
|
||||
*
|
||||
* @return string key/local name of current element
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->folders);
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::current()
|
||||
*
|
||||
* @return \Laminas\Mail\Storage\Folder current folder
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->folders);
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::rewind()
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->folders);
|
||||
}
|
||||
|
||||
/**
|
||||
* get subfolder named $name
|
||||
*
|
||||
* @param string $name wanted subfolder
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return \Laminas\Mail\Storage\Folder folder named $folder
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (! isset($this->folders[$name])) {
|
||||
throw new Exception\InvalidArgumentException("no subfolder named $name");
|
||||
}
|
||||
|
||||
return $this->folders[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* add or replace subfolder named $name
|
||||
*
|
||||
* @param string $name local name of subfolder
|
||||
* @param \Laminas\Mail\Storage\Folder $folder instance for new subfolder
|
||||
*/
|
||||
public function __set($name, Folder $folder)
|
||||
{
|
||||
$this->folders[$name] = $folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove subfolder named $name
|
||||
*
|
||||
* @param string $name local name of subfolder
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
unset($this->folders[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* magic method for easy output of global name
|
||||
*
|
||||
* @return string global name of folder
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->getGlobalName();
|
||||
}
|
||||
|
||||
/**
|
||||
* get local name
|
||||
*
|
||||
* @return string local name
|
||||
*/
|
||||
public function getLocalName()
|
||||
{
|
||||
return $this->localName;
|
||||
}
|
||||
|
||||
/**
|
||||
* get global name
|
||||
*
|
||||
* @return string global name
|
||||
*/
|
||||
public function getGlobalName()
|
||||
{
|
||||
return $this->globalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* is this folder selectable?
|
||||
*
|
||||
* @return bool selectable
|
||||
*/
|
||||
public function isSelectable()
|
||||
{
|
||||
return $this->selectable;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if folder has no subfolder
|
||||
*
|
||||
* @return bool true if no subfolders
|
||||
*/
|
||||
public function isLeaf()
|
||||
{
|
||||
return empty($this->folders);
|
||||
}
|
||||
}
|
||||
38
vendor/laminas/laminas-mail/src/Storage/Folder/FolderInterface.php
vendored
Normal file
38
vendor/laminas/laminas-mail/src/Storage/Folder/FolderInterface.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Folder;
|
||||
|
||||
interface FolderInterface
|
||||
{
|
||||
/**
|
||||
* get root folder or given folder
|
||||
*
|
||||
* @param string $rootFolder get folder structure for given folder, else root
|
||||
* @return FolderInterface root or wanted folder
|
||||
*/
|
||||
public function getFolders($rootFolder = null);
|
||||
|
||||
/**
|
||||
* select given folder
|
||||
*
|
||||
* folder must be selectable!
|
||||
*
|
||||
* @param FolderInterface|string $globalName global name of folder or instance for subfolder
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function selectFolder($globalName);
|
||||
|
||||
/**
|
||||
* get Laminas\Mail\Storage\Folder instance for current folder
|
||||
*
|
||||
* @return FolderInterface instance of current folder
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getCurrentFolder();
|
||||
}
|
||||
216
vendor/laminas/laminas-mail/src/Storage/Folder/Maildir.php
vendored
Normal file
216
vendor/laminas/laminas-mail/src/Storage/Folder/Maildir.php
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Folder;
|
||||
|
||||
use Laminas\Mail\Storage;
|
||||
use Laminas\Mail\Storage\Exception;
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
|
||||
class Maildir extends Storage\Maildir implements FolderInterface
|
||||
{
|
||||
/**
|
||||
* root folder for folder structure
|
||||
* @var Storage\Folder
|
||||
*/
|
||||
protected $rootFolder;
|
||||
|
||||
/**
|
||||
* rootdir of folder structure
|
||||
* @var string
|
||||
*/
|
||||
protected $rootdir;
|
||||
|
||||
/**
|
||||
* name of current folder
|
||||
* @var string
|
||||
*/
|
||||
protected $currentFolder;
|
||||
|
||||
/**
|
||||
* delim char for subfolders
|
||||
* @var string
|
||||
*/
|
||||
protected $delim;
|
||||
|
||||
/**
|
||||
* Create instance with parameters
|
||||
*
|
||||
* Supported parameters are:
|
||||
*
|
||||
* - dirname rootdir of maildir structure
|
||||
* - delim delim char for folder structure, default is '.'
|
||||
* - folder initial selected folder, default is 'INBOX'
|
||||
*
|
||||
* @param $params array mail reader specific parameters
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
$params = (object) $params;
|
||||
}
|
||||
|
||||
if (! isset($params->dirname) || ! is_dir($params->dirname)) {
|
||||
throw new Exception\InvalidArgumentException('no valid dirname given in params');
|
||||
}
|
||||
|
||||
$this->rootdir = rtrim($params->dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
|
||||
$this->delim = isset($params->delim) ? $params->delim : '.';
|
||||
|
||||
$this->buildFolderTree();
|
||||
$this->selectFolder(! empty($params->folder) ? $params->folder : 'INBOX');
|
||||
$this->has['top'] = true;
|
||||
$this->has['flags'] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* find all subfolders and mbox files for folder structure
|
||||
*
|
||||
* Result is save in Storage\Folder instances with the root in $this->rootFolder.
|
||||
* $parentFolder and $parentGlobalName are only used internally for recursion.
|
||||
*
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
protected function buildFolderTree()
|
||||
{
|
||||
$this->rootFolder = new Storage\Folder('/', '/', false);
|
||||
$this->rootFolder->INBOX = new Storage\Folder('INBOX', 'INBOX', true);
|
||||
|
||||
ErrorHandler::start(E_WARNING);
|
||||
$dh = opendir($this->rootdir);
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $dh) {
|
||||
throw new Exception\RuntimeException("can't read folders in maildir", 0, $error);
|
||||
}
|
||||
$dirs = [];
|
||||
|
||||
while (($entry = readdir($dh)) !== false) {
|
||||
// maildir++ defines folders must start with .
|
||||
if ($entry[0] != '.' || $entry == '.' || $entry == '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->isMaildir($this->rootdir . $entry)) {
|
||||
$dirs[] = $entry;
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
|
||||
sort($dirs);
|
||||
$stack = [null];
|
||||
$folderStack = [null];
|
||||
$parentFolder = $this->rootFolder;
|
||||
$parent = '.';
|
||||
|
||||
foreach ($dirs as $dir) {
|
||||
do {
|
||||
if (strpos($dir, $parent) === 0) {
|
||||
$local = substr($dir, strlen($parent));
|
||||
if (strpos($local, $this->delim) !== false) {
|
||||
throw new Exception\RuntimeException('error while reading maildir');
|
||||
}
|
||||
array_push($stack, $parent);
|
||||
$parent = $dir . $this->delim;
|
||||
$folder = new Storage\Folder($local, substr($dir, 1), true);
|
||||
$parentFolder->$local = $folder;
|
||||
array_push($folderStack, $parentFolder);
|
||||
$parentFolder = $folder;
|
||||
break;
|
||||
} elseif ($stack) {
|
||||
$parent = array_pop($stack);
|
||||
$parentFolder = array_pop($folderStack);
|
||||
}
|
||||
} while ($stack);
|
||||
if (! $stack) {
|
||||
throw new Exception\RuntimeException('error while reading maildir');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get root folder or given folder
|
||||
*
|
||||
* @param string $rootFolder get folder structure for given folder, else root
|
||||
* @throws \Laminas\Mail\Storage\Exception\InvalidArgumentException
|
||||
* @return \Laminas\Mail\Storage\Folder root or wanted folder
|
||||
*/
|
||||
public function getFolders($rootFolder = null)
|
||||
{
|
||||
if (! $rootFolder || $rootFolder == 'INBOX') {
|
||||
return $this->rootFolder;
|
||||
}
|
||||
|
||||
// rootdir is same as INBOX in maildir
|
||||
if (strpos($rootFolder, 'INBOX' . $this->delim) === 0) {
|
||||
$rootFolder = substr($rootFolder, 6);
|
||||
}
|
||||
$currentFolder = $this->rootFolder;
|
||||
$subname = trim($rootFolder, $this->delim);
|
||||
|
||||
while ($currentFolder) {
|
||||
ErrorHandler::start(E_NOTICE);
|
||||
list($entry, $subname) = explode($this->delim, $subname, 2);
|
||||
ErrorHandler::stop();
|
||||
$currentFolder = $currentFolder->$entry;
|
||||
if (! $subname) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($currentFolder->getGlobalName() != rtrim($rootFolder, $this->delim)) {
|
||||
throw new Exception\InvalidArgumentException("folder $rootFolder not found");
|
||||
}
|
||||
return $currentFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* select given folder
|
||||
*
|
||||
* folder must be selectable!
|
||||
*
|
||||
* @param Storage\Folder|string $globalName global name of folder or
|
||||
* instance for subfolder
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function selectFolder($globalName)
|
||||
{
|
||||
$this->currentFolder = (string) $globalName;
|
||||
|
||||
// getting folder from folder tree for validation
|
||||
$folder = $this->getFolders($this->currentFolder);
|
||||
|
||||
try {
|
||||
$this->openMaildir($this->rootdir . '.' . $folder->getGlobalName());
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
// check what went wrong
|
||||
if (! $folder->isSelectable()) {
|
||||
throw new Exception\RuntimeException("{$this->currentFolder} is not selectable", 0, $e);
|
||||
}
|
||||
// seems like file has vanished; rebuilding folder tree - but it's still an exception
|
||||
$this->buildFolderTree();
|
||||
throw new Exception\RuntimeException(
|
||||
'seems like the maildir has vanished; I have rebuilt the folder tree; '
|
||||
. 'search for another folder and try again',
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get Storage\Folder instance for current folder
|
||||
*
|
||||
* @return Storage\Folder instance of current folder
|
||||
*/
|
||||
public function getCurrentFolder()
|
||||
{
|
||||
return $this->currentFolder;
|
||||
}
|
||||
}
|
||||
213
vendor/laminas/laminas-mail/src/Storage/Folder/Mbox.php
vendored
Normal file
213
vendor/laminas/laminas-mail/src/Storage/Folder/Mbox.php
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Folder;
|
||||
|
||||
use Laminas\Mail\Storage;
|
||||
use Laminas\Mail\Storage\Exception;
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
|
||||
class Mbox extends Storage\Mbox implements FolderInterface
|
||||
{
|
||||
/**
|
||||
* Storage\Folder root folder for folder structure
|
||||
* @var Storage\Folder
|
||||
*/
|
||||
protected $rootFolder;
|
||||
|
||||
/**
|
||||
* rootdir of folder structure
|
||||
* @var string
|
||||
*/
|
||||
protected $rootdir;
|
||||
|
||||
/**
|
||||
* name of current folder
|
||||
* @var string
|
||||
*/
|
||||
protected $currentFolder;
|
||||
|
||||
/**
|
||||
* Create instance with parameters
|
||||
*
|
||||
* Disallowed parameters are:
|
||||
* - filename use \Laminas\Mail\Storage\Mbox for a single file
|
||||
*
|
||||
* Supported parameters are:
|
||||
*
|
||||
* - dirname rootdir of mbox structure
|
||||
* - folder initial selected folder, default is 'INBOX'
|
||||
*
|
||||
* @param $params array mail reader specific parameters
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
$params = (object) $params;
|
||||
}
|
||||
|
||||
if (isset($params->filename)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf('use %s for a single file', Storage\Mbox::class));
|
||||
}
|
||||
|
||||
if (! isset($params->dirname) || ! is_dir($params->dirname)) {
|
||||
throw new Exception\InvalidArgumentException('no valid dirname given in params');
|
||||
}
|
||||
|
||||
$this->rootdir = rtrim($params->dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
|
||||
$this->buildFolderTree($this->rootdir);
|
||||
$this->selectFolder(! empty($params->folder) ? $params->folder : 'INBOX');
|
||||
$this->has['top'] = true;
|
||||
$this->has['uniqueid'] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* find all subfolders and mbox files for folder structure
|
||||
*
|
||||
* Result is save in Storage\Folder instances with the root in $this->rootFolder.
|
||||
* $parentFolder and $parentGlobalName are only used internally for recursion.
|
||||
*
|
||||
* @param string $currentDir call with root dir, also used for recursion.
|
||||
* @param Storage\Folder|null $parentFolder used for recursion
|
||||
* @param string $parentGlobalName used for recursion
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function buildFolderTree($currentDir, $parentFolder = null, $parentGlobalName = '')
|
||||
{
|
||||
if (! $parentFolder) {
|
||||
$this->rootFolder = new Storage\Folder('/', '/', false);
|
||||
$parentFolder = $this->rootFolder;
|
||||
}
|
||||
|
||||
ErrorHandler::start(E_WARNING);
|
||||
$dh = opendir($currentDir);
|
||||
ErrorHandler::stop();
|
||||
if (! $dh) {
|
||||
throw new Exception\InvalidArgumentException("can't read dir $currentDir");
|
||||
}
|
||||
while (($entry = readdir($dh)) !== false) {
|
||||
// ignore hidden files for mbox
|
||||
if ($entry[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
$absoluteEntry = $currentDir . $entry;
|
||||
$globalName = $parentGlobalName . DIRECTORY_SEPARATOR . $entry;
|
||||
if (is_file($absoluteEntry) && $this->isMboxFile($absoluteEntry)) {
|
||||
$parentFolder->$entry = new Storage\Folder($entry, $globalName);
|
||||
continue;
|
||||
}
|
||||
if (! is_dir($absoluteEntry) /* || $entry == '.' || $entry == '..' */) {
|
||||
continue;
|
||||
}
|
||||
$folder = new Storage\Folder($entry, $globalName, false);
|
||||
$parentFolder->$entry = $folder;
|
||||
$this->buildFolderTree($absoluteEntry . DIRECTORY_SEPARATOR, $folder, $globalName);
|
||||
}
|
||||
|
||||
closedir($dh);
|
||||
}
|
||||
|
||||
/**
|
||||
* get root folder or given folder
|
||||
*
|
||||
* @param string $rootFolder get folder structure for given folder, else root
|
||||
* @return Storage\Folder root or wanted folder
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function getFolders($rootFolder = null)
|
||||
{
|
||||
if (! $rootFolder) {
|
||||
return $this->rootFolder;
|
||||
}
|
||||
|
||||
$currentFolder = $this->rootFolder;
|
||||
$subname = trim($rootFolder, DIRECTORY_SEPARATOR);
|
||||
while ($currentFolder) {
|
||||
ErrorHandler::start(E_NOTICE);
|
||||
list($entry, $subname) = explode(DIRECTORY_SEPARATOR, $subname, 2);
|
||||
ErrorHandler::stop();
|
||||
$currentFolder = $currentFolder->$entry;
|
||||
if (! $subname) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($currentFolder->getGlobalName() != DIRECTORY_SEPARATOR . trim($rootFolder, DIRECTORY_SEPARATOR)) {
|
||||
throw new Exception\InvalidArgumentException("folder $rootFolder not found");
|
||||
}
|
||||
return $currentFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* select given folder
|
||||
*
|
||||
* folder must be selectable!
|
||||
*
|
||||
* @param Storage\Folder|string $globalName global name of folder or
|
||||
* instance for subfolder
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function selectFolder($globalName)
|
||||
{
|
||||
$this->currentFolder = (string) $globalName;
|
||||
|
||||
// getting folder from folder tree for validation
|
||||
$folder = $this->getFolders($this->currentFolder);
|
||||
|
||||
try {
|
||||
$this->openMboxFile($this->rootdir . $folder->getGlobalName());
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
// check what went wrong
|
||||
if (! $folder->isSelectable()) {
|
||||
throw new Exception\RuntimeException("{$this->currentFolder} is not selectable", 0, $e);
|
||||
}
|
||||
// seems like file has vanished; rebuilding folder tree - but it's still an exception
|
||||
$this->buildFolderTree($this->rootdir);
|
||||
throw new Exception\RuntimeException(
|
||||
'seems like the mbox file has vanished; I have rebuilt the folder tree; '
|
||||
. 'search for another folder and try again',
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get Storage\Folder instance for current folder
|
||||
*
|
||||
* @return Storage\Folder instance of current folder
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
public function getCurrentFolder()
|
||||
{
|
||||
return $this->currentFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* magic method for serialize()
|
||||
*
|
||||
* with this method you can cache the mbox class
|
||||
*
|
||||
* @return array name of variables
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return array_merge(parent::__sleep(), ['currentFolder', 'rootFolder', 'rootdir']);
|
||||
}
|
||||
|
||||
/**
|
||||
* magic method for unserialize(), with this method you can cache the mbox class
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
// if cache is stall selectFolder() rebuilds the tree on error
|
||||
parent::__wakeup();
|
||||
}
|
||||
}
|
||||
551
vendor/laminas/laminas-mail/src/Storage/Imap.php
vendored
Normal file
551
vendor/laminas/laminas-mail/src/Storage/Imap.php
vendored
Normal file
@ -0,0 +1,551 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use Laminas\Mail;
|
||||
use Laminas\Mail\Protocol;
|
||||
|
||||
class Imap extends AbstractStorage implements Folder\FolderInterface, Writable\WritableInterface
|
||||
{
|
||||
// TODO: with an internal cache we could optimize this class, or create an extra class with
|
||||
// such optimizations. Especially the various fetch calls could be combined to one cache call
|
||||
|
||||
/**
|
||||
* protocol handler
|
||||
* @var null|Protocol\Imap
|
||||
*/
|
||||
protected $protocol;
|
||||
|
||||
/**
|
||||
* name of current folder
|
||||
* @var string
|
||||
*/
|
||||
protected $currentFolder = '';
|
||||
|
||||
/**
|
||||
* IMAP folder delimiter character
|
||||
* @var null|string
|
||||
*/
|
||||
protected $delimiter;
|
||||
|
||||
/**
|
||||
* IMAP flags to constants translation
|
||||
* @var array
|
||||
*/
|
||||
protected static $knownFlags = [
|
||||
'\Passed' => Mail\Storage::FLAG_PASSED,
|
||||
'\Answered' => Mail\Storage::FLAG_ANSWERED,
|
||||
'\Seen' => Mail\Storage::FLAG_SEEN,
|
||||
'\Unseen' => Mail\Storage::FLAG_UNSEEN,
|
||||
'\Deleted' => Mail\Storage::FLAG_DELETED,
|
||||
'\Draft' => Mail\Storage::FLAG_DRAFT,
|
||||
'\Flagged' => Mail\Storage::FLAG_FLAGGED,
|
||||
];
|
||||
|
||||
/**
|
||||
* IMAP flags to search criteria
|
||||
* @var array
|
||||
*/
|
||||
protected static $searchFlags = [
|
||||
'\Recent' => 'RECENT',
|
||||
'\Answered' => 'ANSWERED',
|
||||
'\Seen' => 'SEEN',
|
||||
'\Unseen' => 'UNSEEN',
|
||||
'\Deleted' => 'DELETED',
|
||||
'\Draft' => 'DRAFT',
|
||||
'\Flagged' => 'FLAGGED',
|
||||
];
|
||||
|
||||
/**
|
||||
* Count messages all messages in current box
|
||||
*
|
||||
* @param null $flags
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
* @return int number of messages
|
||||
*/
|
||||
public function countMessages($flags = null)
|
||||
{
|
||||
if (! $this->currentFolder) {
|
||||
throw new Exception\RuntimeException('No selected folder to count');
|
||||
}
|
||||
|
||||
if ($flags === null) {
|
||||
return count($this->protocol->search(['ALL']));
|
||||
}
|
||||
|
||||
$params = [];
|
||||
foreach ((array) $flags as $flag) {
|
||||
if (isset(static::$searchFlags[$flag])) {
|
||||
$params[] = static::$searchFlags[$flag];
|
||||
} else {
|
||||
$params[] = 'KEYWORD';
|
||||
$params[] = $this->protocol->escapeString($flag);
|
||||
}
|
||||
}
|
||||
return count($this->protocol->search($params));
|
||||
}
|
||||
|
||||
/**
|
||||
* get a list of messages with number and size
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @return int|array size of given message of list with all messages as [num => size]
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function getSize($id = 0)
|
||||
{
|
||||
if ($id) {
|
||||
return $this->protocol->fetch('RFC822.SIZE', $id);
|
||||
}
|
||||
return $this->protocol->fetch('RFC822.SIZE', 1, INF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @return Message
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function getMessage($id)
|
||||
{
|
||||
$data = $this->protocol->fetch(['FLAGS', 'RFC822.HEADER'], $id);
|
||||
$header = $data['RFC822.HEADER'];
|
||||
|
||||
$flags = [];
|
||||
foreach ($data['FLAGS'] as $flag) {
|
||||
$flags[] = isset(static::$knownFlags[$flag]) ? static::$knownFlags[$flag] : $flag;
|
||||
}
|
||||
|
||||
return new $this->messageClass(['handler' => $this, 'id' => $id, 'headers' => $header, 'flags' => $flags]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw header of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message header
|
||||
* @param int $topLines include this many lines with header (after an empty line)
|
||||
* @param int $topLines include this many lines with header (after an empty line)
|
||||
* @return string raw header
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function getRawHeader($id, $part = null, $topLines = 0)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
|
||||
// TODO: toplines
|
||||
return $this->protocol->fetch('RFC822.HEADER', $id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw content of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message content
|
||||
* @return string raw content
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function getRawContent($id, $part = null)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
|
||||
return $this->protocol->fetch('RFC822.TEXT', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* create instance with parameters
|
||||
*
|
||||
* Supported parameters are
|
||||
*
|
||||
* - user username
|
||||
* - host hostname or ip address of IMAP server [optional, default = 'localhost']
|
||||
* - password password for user 'username' [optional, default = '']
|
||||
* - port port for IMAP server [optional, default = 110]
|
||||
* - ssl 'SSL' or 'TLS' for secure sockets
|
||||
* - folder select this folder [optional, default = 'INBOX']
|
||||
*
|
||||
* @param array|Protocol\Imap $params mail reader specific parameters or configured Imap protocol object
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
$params = (object) $params;
|
||||
}
|
||||
|
||||
$this->has['flags'] = true;
|
||||
|
||||
if ($params instanceof Protocol\Imap) {
|
||||
$this->protocol = $params;
|
||||
try {
|
||||
$this->selectFolder('INBOX');
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
throw new Exception\RuntimeException('cannot select INBOX, is this a valid transport?', 0, $e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset($params->user)) {
|
||||
throw new Exception\InvalidArgumentException('need at least user in params');
|
||||
}
|
||||
|
||||
$host = isset($params->host) ? $params->host : 'localhost';
|
||||
$password = isset($params->password) ? $params->password : '';
|
||||
$port = isset($params->port) ? $params->port : null;
|
||||
$ssl = isset($params->ssl) ? $params->ssl : false;
|
||||
|
||||
$this->protocol = new Protocol\Imap();
|
||||
|
||||
if (isset($params->novalidatecert)) {
|
||||
$this->protocol->setNoValidateCert((bool)$params->novalidatecert);
|
||||
}
|
||||
|
||||
$this->protocol->connect($host, $port, $ssl);
|
||||
if (! $this->protocol->login($params->user, $password)) {
|
||||
throw new Exception\RuntimeException('cannot login, user or password wrong');
|
||||
}
|
||||
$this->selectFolder(isset($params->folder) ? $params->folder : 'INBOX');
|
||||
}
|
||||
|
||||
/**
|
||||
* Close resource for mail lib.
|
||||
*
|
||||
* If you need to control, when the resource is closed. Otherwise the
|
||||
* destructor would call this.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->currentFolder = '';
|
||||
$this->protocol->logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the server busy.
|
||||
*
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function noop()
|
||||
{
|
||||
if (! $this->protocol->noop()) {
|
||||
throw new Exception\RuntimeException('could not do nothing');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a message from server.
|
||||
*
|
||||
* If you're doing that from a web environment you should be careful and
|
||||
* use a uniqueid as parameter if possible to identify the message.
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function removeMessage($id)
|
||||
{
|
||||
if (! $this->protocol->store([Mail\Storage::FLAG_DELETED], $id, null, '+')) {
|
||||
throw new Exception\RuntimeException('cannot set deleted flag');
|
||||
}
|
||||
// TODO: expunge here or at close? we can handle an error here better and are more fail safe
|
||||
if (! $this->protocol->expunge()) {
|
||||
throw new Exception\RuntimeException('message marked as deleted, but could not expunge');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get unique id for one or all messages
|
||||
*
|
||||
* if storage does not support unique ids it's the same as the message
|
||||
* number.
|
||||
*
|
||||
* @param int|null $id message number
|
||||
* @return array|string message number for given message or all messages as array
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function getUniqueId($id = null)
|
||||
{
|
||||
if ($id) {
|
||||
return $this->protocol->fetch('UID', $id);
|
||||
}
|
||||
|
||||
return $this->protocol->fetch('UID', 1, INF);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a message number from a unique id
|
||||
*
|
||||
* I.e. if you have a webmailer that supports deleting messages you should
|
||||
* use unique ids as parameter and use this method to translate it to
|
||||
* message number right before calling removeMessage()
|
||||
*
|
||||
* @param string $id unique id
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return int message number
|
||||
*/
|
||||
public function getNumberByUniqueId($id)
|
||||
{
|
||||
// TODO: use search to find number directly
|
||||
$ids = $this->getUniqueId();
|
||||
foreach ($ids as $k => $v) {
|
||||
if ($v == $id) {
|
||||
return $k;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception\InvalidArgumentException('unique id not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* get root folder or given folder
|
||||
*
|
||||
* @param string $rootFolder get folder structure for given folder, else root
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
* @return Folder root or wanted folder
|
||||
*/
|
||||
public function getFolders($rootFolder = null)
|
||||
{
|
||||
$folders = $this->protocol->listMailbox((string) $rootFolder);
|
||||
if (! $folders) {
|
||||
throw new Exception\InvalidArgumentException('folder not found');
|
||||
}
|
||||
|
||||
ksort($folders, SORT_STRING);
|
||||
$root = new Folder('/', '/', false);
|
||||
$stack = [null];
|
||||
$folderStack = [null];
|
||||
$parentFolder = $root;
|
||||
$parent = '';
|
||||
|
||||
foreach ($folders as $globalName => $data) {
|
||||
do {
|
||||
if (! $parent || strpos($globalName, $parent) === 0) {
|
||||
$pos = strrpos($globalName, $data['delim']);
|
||||
if ($pos === false) {
|
||||
$localName = $globalName;
|
||||
} else {
|
||||
$localName = substr($globalName, $pos + 1);
|
||||
}
|
||||
$selectable = ! $data['flags'] || ! in_array('\\Noselect', $data['flags']);
|
||||
|
||||
array_push($stack, $parent);
|
||||
$parent = $globalName . $data['delim'];
|
||||
$folder = new Folder($localName, $globalName, $selectable);
|
||||
$parentFolder->$localName = $folder;
|
||||
array_push($folderStack, $parentFolder);
|
||||
$parentFolder = $folder;
|
||||
$this->delimiter = $data['delim'];
|
||||
break;
|
||||
} elseif ($stack) {
|
||||
$parent = array_pop($stack);
|
||||
$parentFolder = array_pop($folderStack);
|
||||
}
|
||||
} while ($stack);
|
||||
if (! $stack) {
|
||||
throw new Exception\RuntimeException('error while constructing folder tree');
|
||||
}
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* select given folder
|
||||
*
|
||||
* folder must be selectable!
|
||||
*
|
||||
* @param Folder|string $globalName global name of folder or instance for subfolder
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function selectFolder($globalName)
|
||||
{
|
||||
$this->currentFolder = $globalName;
|
||||
if (! $this->protocol->select($this->currentFolder)) {
|
||||
$this->currentFolder = '';
|
||||
throw new Exception\RuntimeException('cannot change folder, maybe it does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get Folder instance for current folder
|
||||
*
|
||||
* @return Folder instance of current folder
|
||||
*/
|
||||
public function getCurrentFolder()
|
||||
{
|
||||
return $this->currentFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new folder
|
||||
*
|
||||
* This method also creates parent folders if necessary. Some mail storages
|
||||
* may restrict, which folder may be used as parent or which chars may be
|
||||
* used in the folder name
|
||||
*
|
||||
* @param string $name global name of folder, local name if $parentFolder
|
||||
* is set
|
||||
* @param string|Folder $parentFolder parent folder for new folder, else
|
||||
* root folder is parent
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function createFolder($name, $parentFolder = null)
|
||||
{
|
||||
// TODO: we assume / as the hierarchy delim - need to get that from the folder class!
|
||||
if ($parentFolder instanceof Folder) {
|
||||
$folder = $parentFolder->getGlobalName() . '/' . $name;
|
||||
} elseif ($parentFolder !== null) {
|
||||
$folder = $parentFolder . '/' . $name;
|
||||
} else {
|
||||
$folder = $name;
|
||||
}
|
||||
|
||||
if (! $this->protocol->create($folder)) {
|
||||
throw new Exception\RuntimeException('cannot create folder');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a folder
|
||||
*
|
||||
* @param string|Folder $name name or instance of folder
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function removeFolder($name)
|
||||
{
|
||||
if ($name instanceof Folder) {
|
||||
$name = $name->getGlobalName();
|
||||
}
|
||||
|
||||
if (! $this->protocol->delete($name)) {
|
||||
throw new Exception\RuntimeException('cannot delete folder');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rename and/or move folder
|
||||
*
|
||||
* The new name has the same restrictions as in createFolder()
|
||||
*
|
||||
* @param string|Folder $oldName name or instance of folder
|
||||
* @param string $newName new global name of folder
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function renameFolder($oldName, $newName)
|
||||
{
|
||||
if ($oldName instanceof Folder) {
|
||||
$oldName = $oldName->getGlobalName();
|
||||
}
|
||||
|
||||
if (! $this->protocol->rename($oldName, $newName)) {
|
||||
throw new Exception\RuntimeException('cannot rename folder');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* append a new message to mail storage
|
||||
*
|
||||
* @param string $message message as string or instance of message class
|
||||
* @param null|string|Folder $folder folder for new message, else current
|
||||
* folder is taken
|
||||
* @param null|array $flags set flags for new message, else a default set
|
||||
* is used
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function appendMessage($message, $folder = null, $flags = null)
|
||||
{
|
||||
if ($folder === null) {
|
||||
$folder = $this->currentFolder;
|
||||
}
|
||||
|
||||
if ($flags === null) {
|
||||
$flags = [Mail\Storage::FLAG_SEEN];
|
||||
}
|
||||
|
||||
// TODO: handle class instances for $message
|
||||
if (! $this->protocol->append($folder, $message, $flags)) {
|
||||
throw new Exception\RuntimeException(
|
||||
'cannot create message, please check if the folder exists and your flags'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* copy an existing message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param string|Folder $folder name or instance of target folder
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function copyMessage($id, $folder)
|
||||
{
|
||||
if (! $this->protocol->copy($folder, $id)) {
|
||||
throw new Exception\RuntimeException('cannot copy message, does the folder exist?');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* move an existing message
|
||||
*
|
||||
* NOTE: IMAP has no native move command, thus it's emulated with copy and delete
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param string|Folder $folder name or instance of target folder
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function moveMessage($id, $folder)
|
||||
{
|
||||
$this->copyMessage($id, $folder);
|
||||
$this->removeMessage($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* set flags for message
|
||||
*
|
||||
* NOTE: this method can't set the recent flag.
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param array $flags new flags for message
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function setFlags($id, $flags)
|
||||
{
|
||||
if (! $this->protocol->store($flags, $id)) {
|
||||
throw new Exception\RuntimeException(
|
||||
'cannot set flags, have you tried to set the recent flag or special chars?'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get IMAP delimiter
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function delimiter()
|
||||
{
|
||||
if (! isset($this->delimiter)) {
|
||||
$this->getFolders();
|
||||
}
|
||||
return $this->delimiter;
|
||||
}
|
||||
}
|
||||
416
vendor/laminas/laminas-mail/src/Storage/Maildir.php
vendored
Normal file
416
vendor/laminas/laminas-mail/src/Storage/Maildir.php
vendored
Normal file
@ -0,0 +1,416 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use Laminas\Mail;
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
|
||||
class Maildir extends AbstractStorage
|
||||
{
|
||||
/**
|
||||
* used message class, change it in an extended class to extend the returned message class
|
||||
* @var string
|
||||
*/
|
||||
protected $messageClass = Message\File::class;
|
||||
|
||||
/**
|
||||
* data of found message files in maildir dir
|
||||
* @var array
|
||||
*/
|
||||
protected $files = [];
|
||||
|
||||
/**
|
||||
* known flag chars in filenames
|
||||
*
|
||||
* This list has to be in alphabetical order for setFlags()
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $knownFlags = [
|
||||
'D' => Mail\Storage::FLAG_DRAFT,
|
||||
'F' => Mail\Storage::FLAG_FLAGGED,
|
||||
'P' => Mail\Storage::FLAG_PASSED,
|
||||
'R' => Mail\Storage::FLAG_ANSWERED,
|
||||
'S' => Mail\Storage::FLAG_SEEN,
|
||||
'T' => Mail\Storage::FLAG_DELETED,
|
||||
];
|
||||
|
||||
// TODO: getFlags($id) for fast access if headers are not needed (i.e. just setting flags)?
|
||||
|
||||
/**
|
||||
* Count messages all messages in current box
|
||||
*
|
||||
* @param mixed $flags
|
||||
* @return int number of messages
|
||||
*/
|
||||
public function countMessages($flags = null)
|
||||
{
|
||||
if ($flags === null) {
|
||||
return count($this->files);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
if (! is_array($flags)) {
|
||||
foreach ($this->files as $file) {
|
||||
if (isset($file['flaglookup'][$flags])) {
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
$flags = array_flip($flags);
|
||||
foreach ($this->files as $file) {
|
||||
foreach ($flags as $flag => $v) {
|
||||
if (! isset($file['flaglookup'][$flag])) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
++$count;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one or all fields from file structure. Also checks if message is valid
|
||||
*
|
||||
* @param int $id message number
|
||||
* @param string|null $field wanted field
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return string|array wanted field or all fields as array
|
||||
*/
|
||||
protected function getFileData($id, $field = null)
|
||||
{
|
||||
if (! isset($this->files[$id - 1])) {
|
||||
throw new Exception\InvalidArgumentException('id does not exist');
|
||||
}
|
||||
|
||||
if (! $field) {
|
||||
return $this->files[$id - 1];
|
||||
}
|
||||
|
||||
if (! isset($this->files[$id - 1][$field])) {
|
||||
throw new Exception\InvalidArgumentException('field does not exist');
|
||||
}
|
||||
|
||||
return $this->files[$id - 1][$field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of messages with number and size
|
||||
*
|
||||
* @param int|null $id number of message or null for all messages
|
||||
* @return int|array size of given message of list with all messages as array(num => size)
|
||||
*/
|
||||
public function getSize($id = null)
|
||||
{
|
||||
if ($id !== null) {
|
||||
$filedata = $this->getFileData($id);
|
||||
return isset($filedata['size']) ? $filedata['size'] : filesize($filedata['filename']);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($this->files as $num => $data) {
|
||||
$result[$num + 1] = isset($data['size']) ? $data['size'] : filesize($data['filename']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @return \Laminas\Mail\Storage\Message\File
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getMessage($id)
|
||||
{
|
||||
// TODO that's ugly, would be better to let the message class decide
|
||||
if (\trim($this->messageClass, '\\') === Message\File::class
|
||||
|| is_subclass_of($this->messageClass, Message\File::class)
|
||||
) {
|
||||
return new $this->messageClass([
|
||||
'file' => $this->getFileData($id, 'filename'),
|
||||
'flags' => $this->getFileData($id, 'flags'),
|
||||
]);
|
||||
}
|
||||
|
||||
return new $this->messageClass([
|
||||
'handler' => $this,
|
||||
'id' => $id,
|
||||
'headers' => $this->getRawHeader($id),
|
||||
'flags' => $this->getFileData($id, 'flags'),
|
||||
]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw header of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message header
|
||||
* @param int $topLines include this many lines with header (after an empty line)
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string raw header
|
||||
*/
|
||||
public function getRawHeader($id, $part = null, $topLines = 0)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
|
||||
$fh = fopen($this->getFileData($id, 'filename'), 'r');
|
||||
|
||||
$content = '';
|
||||
while (! feof($fh)) {
|
||||
$line = fgets($fh);
|
||||
if (! trim($line)) {
|
||||
break;
|
||||
}
|
||||
$content .= $line;
|
||||
}
|
||||
|
||||
fclose($fh);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw content of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message content
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string raw content
|
||||
*/
|
||||
public function getRawContent($id, $part = null)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
|
||||
$fh = fopen($this->getFileData($id, 'filename'), 'r');
|
||||
|
||||
while (! feof($fh)) {
|
||||
$line = fgets($fh);
|
||||
if (! trim($line)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$content = stream_get_contents($fh);
|
||||
fclose($fh);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create instance with parameters
|
||||
* Supported parameters are:
|
||||
* - dirname dirname of mbox file
|
||||
*
|
||||
* @param $params array mail reader specific parameters
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
$params = (object) $params;
|
||||
}
|
||||
|
||||
if (! isset($params->dirname) || ! is_dir($params->dirname)) {
|
||||
throw new Exception\InvalidArgumentException('no valid dirname given in params');
|
||||
}
|
||||
|
||||
if (! $this->isMaildir($params->dirname)) {
|
||||
throw new Exception\InvalidArgumentException('invalid maildir given');
|
||||
}
|
||||
|
||||
$this->has['top'] = true;
|
||||
$this->has['flags'] = true;
|
||||
$this->openMaildir($params->dirname);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a given dir is a valid maildir
|
||||
*
|
||||
* @param string $dirname name of dir
|
||||
* @return bool dir is valid maildir
|
||||
*/
|
||||
protected function isMaildir($dirname)
|
||||
{
|
||||
if (file_exists($dirname . '/new') && ! is_dir($dirname . '/new')) {
|
||||
return false;
|
||||
}
|
||||
if (file_exists($dirname . '/tmp') && ! is_dir($dirname . '/tmp')) {
|
||||
return false;
|
||||
}
|
||||
return is_dir($dirname . '/cur');
|
||||
}
|
||||
|
||||
/**
|
||||
* open given dir as current maildir
|
||||
*
|
||||
* @param string $dirname name of maildir
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
protected function openMaildir($dirname)
|
||||
{
|
||||
if ($this->files) {
|
||||
$this->close();
|
||||
}
|
||||
|
||||
ErrorHandler::start(E_WARNING);
|
||||
$dh = opendir($dirname . '/cur/');
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $dh) {
|
||||
throw new Exception\RuntimeException('cannot open maildir', 0, $error);
|
||||
}
|
||||
$this->getMaildirFiles($dh, $dirname . '/cur/');
|
||||
closedir($dh);
|
||||
|
||||
ErrorHandler::start(E_WARNING);
|
||||
$dh = opendir($dirname . '/new/');
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $dh) {
|
||||
throw new Exception\RuntimeException('cannot read recent mails in maildir', 0, $error);
|
||||
}
|
||||
|
||||
$this->getMaildirFiles($dh, $dirname . '/new/', [Mail\Storage::FLAG_RECENT]);
|
||||
closedir($dh);
|
||||
}
|
||||
|
||||
/**
|
||||
* find all files in opened dir handle and add to maildir files
|
||||
*
|
||||
* @param resource $dh dir handle used for search
|
||||
* @param string $dirname dirname of dir in $dh
|
||||
* @param array $defaultFlags default flags for given dir
|
||||
*/
|
||||
protected function getMaildirFiles($dh, $dirname, $defaultFlags = [])
|
||||
{
|
||||
while (($entry = readdir($dh)) !== false) {
|
||||
if ($entry[0] == '.' || ! is_file($dirname . $entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ErrorHandler::start(E_NOTICE);
|
||||
list($uniq, $info) = explode(':', $entry, 2);
|
||||
list(, $size) = explode(',', $uniq, 2);
|
||||
ErrorHandler::stop();
|
||||
if ($size && $size[0] == 'S' && $size[1] == '=') {
|
||||
$size = substr($size, 2);
|
||||
}
|
||||
if (! ctype_digit($size)) {
|
||||
$size = null;
|
||||
}
|
||||
|
||||
ErrorHandler::start(E_NOTICE);
|
||||
list($version, $flags) = explode(',', $info, 2);
|
||||
ErrorHandler::stop();
|
||||
if ($version != 2) {
|
||||
$flags = '';
|
||||
}
|
||||
|
||||
$namedFlags = $defaultFlags;
|
||||
$length = strlen($flags);
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$flag = $flags[$i];
|
||||
$namedFlags[$flag] = isset(static::$knownFlags[$flag]) ? static::$knownFlags[$flag] : $flag;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'uniq' => $uniq,
|
||||
'flags' => $namedFlags,
|
||||
'flaglookup' => array_flip($namedFlags),
|
||||
'filename' => $dirname . $entry
|
||||
];
|
||||
if ($size !== null) {
|
||||
$data['size'] = (int) $size;
|
||||
}
|
||||
$this->files[] = $data;
|
||||
}
|
||||
\usort($this->files, function ($a, $b) {
|
||||
return \strcmp($a['filename'], $b['filename']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close resource for mail lib. If you need to control, when the resource
|
||||
* is closed. Otherwise the destructor would call this.
|
||||
*
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->files = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Waste some CPU cycles doing nothing.
|
||||
*
|
||||
* @return bool always return true
|
||||
*/
|
||||
public function noop()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* stub for not supported message deletion
|
||||
*
|
||||
* @param $id
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function removeMessage($id)
|
||||
{
|
||||
throw new Exception\RuntimeException('maildir is (currently) read-only');
|
||||
}
|
||||
|
||||
/**
|
||||
* get unique id for one or all messages
|
||||
*
|
||||
* if storage does not support unique ids it's the same as the message number
|
||||
*
|
||||
* @param int|null $id message number
|
||||
* @return array|string message number for given message or all messages as array
|
||||
*/
|
||||
public function getUniqueId($id = null)
|
||||
{
|
||||
if ($id) {
|
||||
return $this->getFileData($id, 'uniq');
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($this->files as $num => $file) {
|
||||
$ids[$num + 1] = $file['uniq'];
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a message number from a unique id
|
||||
*
|
||||
* I.e. if you have a webmailer that supports deleting messages you should use unique ids
|
||||
* as parameter and use this method to translate it to message number right before calling removeMessage()
|
||||
*
|
||||
* @param string $id unique id
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return int message number
|
||||
*/
|
||||
public function getNumberByUniqueId($id)
|
||||
{
|
||||
foreach ($this->files as $num => $file) {
|
||||
if ($file['uniq'] == $id) {
|
||||
return $num + 1;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception\InvalidArgumentException('unique id not found');
|
||||
}
|
||||
}
|
||||
415
vendor/laminas/laminas-mail/src/Storage/Mbox.php
vendored
Normal file
415
vendor/laminas/laminas-mail/src/Storage/Mbox.php
vendored
Normal file
@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
|
||||
class Mbox extends AbstractStorage
|
||||
{
|
||||
/**
|
||||
* file handle to mbox file
|
||||
* @var null|resource
|
||||
*/
|
||||
protected $fh;
|
||||
|
||||
/**
|
||||
* filename of mbox file for __wakeup
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* modification date of mbox file for __wakeup
|
||||
* @var int
|
||||
*/
|
||||
protected $filemtime;
|
||||
|
||||
/**
|
||||
* start and end position of messages as array('start' => start, 'separator' => headersep, 'end' => end)
|
||||
* @var array
|
||||
*/
|
||||
protected $positions;
|
||||
|
||||
/**
|
||||
* used message class, change it in an extended class to extend the returned message class
|
||||
* @var string
|
||||
*/
|
||||
protected $messageClass = Message\File::class;
|
||||
|
||||
/**
|
||||
* end of Line for messages
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $messageEOL;
|
||||
|
||||
/**
|
||||
* Count messages all messages in current box
|
||||
*
|
||||
* @return int number of messages
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function countMessages()
|
||||
{
|
||||
return count($this->positions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of messages with number and size
|
||||
*
|
||||
* @param int|null $id number of message or null for all messages
|
||||
* @return int|array size of given message of list with all messages as array(num => size)
|
||||
*/
|
||||
public function getSize($id = 0)
|
||||
{
|
||||
if ($id) {
|
||||
$pos = $this->positions[$id - 1];
|
||||
return $pos['end'] - $pos['start'];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($this->positions as $num => $pos) {
|
||||
$result[$num + 1] = $pos['end'] - $pos['start'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get positions for mail message or throw exception if id is invalid
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return array positions as in positions
|
||||
*/
|
||||
protected function getPos($id)
|
||||
{
|
||||
if (! isset($this->positions[$id - 1])) {
|
||||
throw new Exception\InvalidArgumentException('id does not exist');
|
||||
}
|
||||
|
||||
return $this->positions[$id - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @return \Laminas\Mail\Storage\Message\File
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getMessage($id)
|
||||
{
|
||||
// TODO that's ugly, would be better to let the message class decide
|
||||
if (is_subclass_of($this->messageClass, Message\File::class)
|
||||
|| strtolower($this->messageClass) === strtolower(Message\File::class)) {
|
||||
// TODO top/body lines
|
||||
$messagePos = $this->getPos($id);
|
||||
|
||||
$messageClassParams = [
|
||||
'file' => $this->fh,
|
||||
'startPos' => $messagePos['start'],
|
||||
'endPos' => $messagePos['end']
|
||||
];
|
||||
|
||||
if (isset($this->messageEOL)) {
|
||||
$messageClassParams['EOL'] = $this->messageEOL;
|
||||
}
|
||||
|
||||
return new $this->messageClass($messageClassParams);
|
||||
}
|
||||
|
||||
$bodyLines = 0; // TODO: need a way to change that
|
||||
|
||||
$message = $this->getRawHeader($id);
|
||||
// file pointer is after headers now
|
||||
if ($bodyLines) {
|
||||
$message .= "\n";
|
||||
while ($bodyLines-- && ftell($this->fh) < $this->positions[$id - 1]['end']) {
|
||||
$message .= fgets($this->fh);
|
||||
}
|
||||
}
|
||||
|
||||
return new $this->messageClass(['handler' => $this, 'id' => $id, 'headers' => $message]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw header of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message header
|
||||
* @param int $topLines include this many lines with header (after an empty line)
|
||||
* @return string raw header
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getRawHeader($id, $part = null, $topLines = 0)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
$messagePos = $this->getPos($id);
|
||||
// TODO: toplines
|
||||
return stream_get_contents($this->fh, $messagePos['separator'] - $messagePos['start'], $messagePos['start']);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw content of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message content
|
||||
* @return string raw content
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getRawContent($id, $part = null)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
$messagePos = $this->getPos($id);
|
||||
return stream_get_contents($this->fh, $messagePos['end'] - $messagePos['separator'], $messagePos['separator']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create instance with parameters
|
||||
* Supported parameters are:
|
||||
* - filename filename of mbox file
|
||||
*
|
||||
* @param $params array mail reader specific parameters
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
$params = (object) $params;
|
||||
}
|
||||
|
||||
if (! isset($params->filename)) {
|
||||
throw new Exception\InvalidArgumentException('no valid filename given in params');
|
||||
}
|
||||
|
||||
if (isset($params->messageEOL)) {
|
||||
$this->messageEOL = (string) $params->messageEOL;
|
||||
}
|
||||
|
||||
$this->openMboxFile($params->filename);
|
||||
$this->has['top'] = true;
|
||||
$this->has['uniqueid'] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if given file is a mbox file
|
||||
*
|
||||
* if $file is a resource its file pointer is moved after the first line
|
||||
*
|
||||
* @param resource|string $file stream resource of name of file
|
||||
* @param bool $fileIsString file is string or resource
|
||||
* @return bool file is mbox file
|
||||
*/
|
||||
protected function isMboxFile($file, $fileIsString = true)
|
||||
{
|
||||
if ($fileIsString) {
|
||||
ErrorHandler::start(E_WARNING);
|
||||
$file = fopen($file, 'r');
|
||||
ErrorHandler::stop();
|
||||
if (! $file) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
fseek($file, 0);
|
||||
}
|
||||
|
||||
$result = false;
|
||||
|
||||
$line = fgets($file) ?: '';
|
||||
if (strpos($line, 'From ') === 0) {
|
||||
$result = true;
|
||||
}
|
||||
|
||||
if ($fileIsString) {
|
||||
ErrorHandler::start(E_WARNING);
|
||||
fclose($file);
|
||||
ErrorHandler::stop();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* open given file as current mbox file
|
||||
*
|
||||
* @param string $filename filename of mbox file
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function openMboxFile($filename)
|
||||
{
|
||||
if ($this->fh) {
|
||||
$this->close();
|
||||
}
|
||||
|
||||
if (is_dir($filename)) {
|
||||
throw new Exception\InvalidArgumentException('file is not a valid mbox file');
|
||||
}
|
||||
|
||||
ErrorHandler::start();
|
||||
$this->fh = fopen($filename, 'r');
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $this->fh) {
|
||||
throw new Exception\RuntimeException('cannot open mbox file', 0, $error);
|
||||
}
|
||||
$this->filename = $filename;
|
||||
$this->filemtime = filemtime($this->filename);
|
||||
|
||||
if (! $this->isMboxFile($this->fh, false)) {
|
||||
ErrorHandler::start(E_WARNING);
|
||||
fclose($this->fh);
|
||||
$error = ErrorHandler::stop();
|
||||
throw new Exception\InvalidArgumentException('file is not a valid mbox format', 0, $error);
|
||||
}
|
||||
|
||||
$messagePos = ['start' => ftell($this->fh), 'separator' => 0, 'end' => 0];
|
||||
while (($line = fgets($this->fh)) !== false) {
|
||||
if (strpos($line, 'From ') === 0) {
|
||||
$messagePos['end'] = ftell($this->fh) - strlen($line) - 2; // + newline
|
||||
if (! $messagePos['separator']) {
|
||||
$messagePos['separator'] = $messagePos['end'];
|
||||
}
|
||||
$this->positions[] = $messagePos;
|
||||
$messagePos = ['start' => ftell($this->fh), 'separator' => 0, 'end' => 0];
|
||||
}
|
||||
if (! $messagePos['separator'] && ! trim($line)) {
|
||||
$messagePos['separator'] = ftell($this->fh);
|
||||
}
|
||||
}
|
||||
|
||||
$messagePos['end'] = ftell($this->fh);
|
||||
if (! $messagePos['separator']) {
|
||||
$messagePos['separator'] = $messagePos['end'];
|
||||
}
|
||||
$this->positions[] = $messagePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close resource for mail lib. If you need to control, when the resource
|
||||
* is closed. Otherwise the destructor would call this.
|
||||
*
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
ErrorHandler::start(E_WARNING);
|
||||
fclose($this->fh);
|
||||
ErrorHandler::stop();
|
||||
$this->positions = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waste some CPU cycles doing nothing.
|
||||
*
|
||||
* @return bool always return true
|
||||
*/
|
||||
public function noop()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* stub for not supported message deletion
|
||||
*
|
||||
* @param $id
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function removeMessage($id)
|
||||
{
|
||||
throw new Exception\RuntimeException('mbox is read-only');
|
||||
}
|
||||
|
||||
/**
|
||||
* get unique id for one or all messages
|
||||
*
|
||||
* Mbox does not support unique ids (yet) - it's always the same as the message number.
|
||||
* That shouldn't be a problem, because we can't change mbox files. Therefor the message
|
||||
* number is save enough.
|
||||
*
|
||||
* @param int|null $id message number
|
||||
* @return array|string message number for given message or all messages as array
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getUniqueId($id = null)
|
||||
{
|
||||
if ($id) {
|
||||
// check if id exists
|
||||
$this->getPos($id);
|
||||
return $id;
|
||||
}
|
||||
|
||||
$range = range(1, $this->countMessages());
|
||||
return array_combine($range, $range);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a message number from a unique id
|
||||
*
|
||||
* I.e. if you have a webmailer that supports deleting messages you should use unique ids
|
||||
* as parameter and use this method to translate it to message number right before calling removeMessage()
|
||||
*
|
||||
* @param string $id unique id
|
||||
* @return int message number
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getNumberByUniqueId($id)
|
||||
{
|
||||
// check if id exists
|
||||
$this->getPos($id);
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* magic method for serialize()
|
||||
*
|
||||
* with this method you can cache the mbox class
|
||||
*
|
||||
* @return array name of variables
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return ['filename', 'positions', 'filemtime'];
|
||||
}
|
||||
|
||||
/**
|
||||
* magic method for unserialize()
|
||||
*
|
||||
* with this method you can cache the mbox class
|
||||
* for cache validation the mtime of the mbox file is used
|
||||
*
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
ErrorHandler::start();
|
||||
$filemtime = filemtime($this->filename);
|
||||
ErrorHandler::stop();
|
||||
if ($this->filemtime != $filemtime) {
|
||||
$this->close();
|
||||
$this->openMboxFile($this->filename);
|
||||
} else {
|
||||
ErrorHandler::start();
|
||||
$this->fh = fopen($this->filename, 'r');
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $this->fh) {
|
||||
throw new Exception\RuntimeException('cannot open mbox file', 0, $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
vendor/laminas/laminas-mail/src/Storage/Message.php
vendored
Normal file
86
vendor/laminas/laminas-mail/src/Storage/Message.php
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
|
||||
class Message extends Part implements Message\MessageInterface
|
||||
{
|
||||
/**
|
||||
* flags for this message
|
||||
* @var array
|
||||
*/
|
||||
protected $flags = [];
|
||||
|
||||
/**
|
||||
* Public constructor
|
||||
*
|
||||
* In addition to the parameters of Part::__construct() this constructor supports:
|
||||
* - file filename or file handle of a file with raw message content
|
||||
* - flags array with flags for message, keys are ignored, use constants defined in \Laminas\Mail\Storage
|
||||
*
|
||||
* @param array $params
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function __construct(array $params)
|
||||
{
|
||||
if (isset($params['file'])) {
|
||||
if (! is_resource($params['file'])) {
|
||||
ErrorHandler::start();
|
||||
$params['raw'] = file_get_contents($params['file']);
|
||||
$error = ErrorHandler::stop();
|
||||
if ($params['raw'] === false) {
|
||||
throw new Exception\RuntimeException('could not open file', 0, $error);
|
||||
}
|
||||
} else {
|
||||
$params['raw'] = stream_get_contents($params['file']);
|
||||
}
|
||||
|
||||
$params['raw'] = ltrim($params['raw']);
|
||||
}
|
||||
|
||||
if (! empty($params['flags'])) {
|
||||
// set key and value to the same value for easy lookup
|
||||
$this->flags = array_combine($params['flags'], $params['flags']);
|
||||
}
|
||||
|
||||
parent::__construct($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* return toplines as found after headers
|
||||
*
|
||||
* @return string toplines
|
||||
*/
|
||||
public function getTopLines()
|
||||
{
|
||||
return $this->topLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if flag is set
|
||||
*
|
||||
* @param mixed $flag a flag name, use constants defined in \Laminas\Mail\Storage
|
||||
* @return bool true if set, otherwise false
|
||||
*/
|
||||
public function hasFlag($flag)
|
||||
{
|
||||
return isset($this->flags[$flag]);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all set flags
|
||||
*
|
||||
* @return array array with flags, key and value are the same for easy lookup
|
||||
*/
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
}
|
||||
70
vendor/laminas/laminas-mail/src/Storage/Message/File.php
vendored
Normal file
70
vendor/laminas/laminas-mail/src/Storage/Message/File.php
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Message;
|
||||
|
||||
use Laminas\Mail\Storage\Part;
|
||||
|
||||
class File extends Part\File implements MessageInterface
|
||||
{
|
||||
/**
|
||||
* flags for this message
|
||||
* @var array
|
||||
*/
|
||||
protected $flags = [];
|
||||
|
||||
/**
|
||||
* Public constructor
|
||||
*
|
||||
* In addition to the parameters of Laminas\Mail\Storage\Part::__construct() this constructor supports:
|
||||
* - flags array with flags for message, keys are ignored, use constants defined in Laminas\Mail\Storage
|
||||
*
|
||||
* @param array $params
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function __construct(array $params)
|
||||
{
|
||||
if (! empty($params['flags'])) {
|
||||
// set key and value to the same value for easy lookup
|
||||
$this->flags = array_combine($params['flags'], $params['flags']);
|
||||
}
|
||||
|
||||
parent::__construct($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* return toplines as found after headers
|
||||
*
|
||||
* @return string toplines
|
||||
*/
|
||||
public function getTopLines()
|
||||
{
|
||||
return $this->topLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if flag is set
|
||||
*
|
||||
* @param mixed $flag a flag name, use constants defined in \Laminas\Mail\Storage
|
||||
* @return bool true if set, otherwise false
|
||||
*/
|
||||
public function hasFlag($flag)
|
||||
{
|
||||
return isset($this->flags[$flag]);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all set flags
|
||||
*
|
||||
* @return array array with flags, key and value are the same for easy lookup
|
||||
*/
|
||||
public function getFlags()
|
||||
{
|
||||
return $this->flags;
|
||||
}
|
||||
}
|
||||
34
vendor/laminas/laminas-mail/src/Storage/Message/MessageInterface.php
vendored
Normal file
34
vendor/laminas/laminas-mail/src/Storage/Message/MessageInterface.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Message;
|
||||
|
||||
interface MessageInterface
|
||||
{
|
||||
/**
|
||||
* return toplines as found after headers
|
||||
*
|
||||
* @return string toplines
|
||||
*/
|
||||
public function getTopLines();
|
||||
|
||||
/**
|
||||
* check if flag is set
|
||||
*
|
||||
* @param mixed $flag a flag name, use constants defined in Laminas\Mail\Storage
|
||||
* @return bool true if set, otherwise false
|
||||
*/
|
||||
public function hasFlag($flag);
|
||||
|
||||
/**
|
||||
* get all set flags
|
||||
*
|
||||
* @return array array with flags, key and value are the same for easy lookup
|
||||
*/
|
||||
public function getFlags();
|
||||
}
|
||||
474
vendor/laminas/laminas-mail/src/Storage/Part.php
vendored
Normal file
474
vendor/laminas/laminas-mail/src/Storage/Part.php
vendored
Normal file
@ -0,0 +1,474 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use Laminas\Mail\Header\HeaderInterface;
|
||||
use Laminas\Mail\Headers;
|
||||
use Laminas\Mime;
|
||||
use RecursiveIterator;
|
||||
|
||||
class Part implements RecursiveIterator, Part\PartInterface
|
||||
{
|
||||
/**
|
||||
* Headers of the part
|
||||
* @var Headers|null
|
||||
*/
|
||||
protected $headers;
|
||||
|
||||
/**
|
||||
* raw part body
|
||||
* @var null|string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* toplines as fetched with headers
|
||||
* @var string
|
||||
*/
|
||||
protected $topLines = '';
|
||||
|
||||
/**
|
||||
* parts of multipart message
|
||||
* @var array
|
||||
*/
|
||||
protected $parts = [];
|
||||
|
||||
/**
|
||||
* count of parts of a multipart message
|
||||
* @var null|int
|
||||
*/
|
||||
protected $countParts;
|
||||
|
||||
/**
|
||||
* current position of iterator
|
||||
* @var int
|
||||
*/
|
||||
protected $iterationPos = 1;
|
||||
|
||||
/**
|
||||
* mail handler, if late fetch is active
|
||||
* @var null|AbstractStorage
|
||||
*/
|
||||
protected $mail;
|
||||
|
||||
/**
|
||||
* message number for mail handler
|
||||
* @var int
|
||||
*/
|
||||
protected $messageNum = 0;
|
||||
|
||||
/**
|
||||
* Public constructor
|
||||
*
|
||||
* Part supports different sources for content. The possible params are:
|
||||
* - handler an instance of AbstractStorage for late fetch
|
||||
* - id number of message for handler
|
||||
* - raw raw content with header and body as string
|
||||
* - headers headers as array (name => value) or string, if a content part is found it's used as toplines
|
||||
* - noToplines ignore content found after headers in param 'headers'
|
||||
* - content content as string
|
||||
* - strict strictly parse raw content
|
||||
*
|
||||
* @param array $params full message with or without headers
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $params)
|
||||
{
|
||||
if (isset($params['handler'])) {
|
||||
if (! $params['handler'] instanceof AbstractStorage) {
|
||||
throw new Exception\InvalidArgumentException('handler is not a valid mail handler');
|
||||
}
|
||||
if (! isset($params['id'])) {
|
||||
throw new Exception\InvalidArgumentException('need a message id with a handler');
|
||||
}
|
||||
|
||||
$this->mail = $params['handler'];
|
||||
$this->messageNum = $params['id'];
|
||||
}
|
||||
|
||||
$params['strict'] = isset($params['strict']) ? $params['strict'] : false;
|
||||
|
||||
if (isset($params['raw'])) {
|
||||
Mime\Decode::splitMessage(
|
||||
$params['raw'],
|
||||
$this->headers,
|
||||
$this->content,
|
||||
Mime\Mime::LINEEND,
|
||||
$params['strict']
|
||||
);
|
||||
} elseif (isset($params['headers'])) {
|
||||
if (is_array($params['headers'])) {
|
||||
$this->headers = new Headers();
|
||||
$this->headers->addHeaders($params['headers']);
|
||||
} else {
|
||||
if (empty($params['noToplines'])) {
|
||||
Mime\Decode::splitMessage($params['headers'], $this->headers, $this->topLines);
|
||||
} else {
|
||||
$this->headers = Headers::fromString($params['headers']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['content'])) {
|
||||
$this->content = $params['content'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if part is a multipart message
|
||||
*
|
||||
* @return bool if part is multipart
|
||||
*/
|
||||
public function isMultipart()
|
||||
{
|
||||
try {
|
||||
return stripos($this->contentType, 'multipart/') === 0;
|
||||
} catch (Exception\ExceptionInterface $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Body of part
|
||||
*
|
||||
* If part is multipart the raw content of this part with all sub parts is returned
|
||||
*
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string body
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if ($this->content !== null) {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
if ($this->mail) {
|
||||
return $this->mail->getRawContent($this->messageNum);
|
||||
}
|
||||
|
||||
throw new Exception\RuntimeException('no content');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return size of part
|
||||
*
|
||||
* Quite simple implemented currently (not decoding). Handle with care.
|
||||
*
|
||||
* @return int size
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return strlen($this->getContent());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache content and split in parts if multipart
|
||||
*
|
||||
* @throws Exception\RuntimeException
|
||||
* @return null
|
||||
*/
|
||||
protected function cacheContent()
|
||||
{
|
||||
// caching content if we can't fetch parts
|
||||
if ($this->content === null && $this->mail) {
|
||||
$this->content = $this->mail->getRawContent($this->messageNum);
|
||||
}
|
||||
|
||||
if (! $this->isMultipart()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split content in parts
|
||||
$boundary = $this->getHeaderField('content-type', 'boundary');
|
||||
if (! $boundary) {
|
||||
throw new Exception\RuntimeException('no boundary found in content type to split message');
|
||||
}
|
||||
$parts = Mime\Decode::splitMessageStruct($this->content, $boundary);
|
||||
if ($parts === null) {
|
||||
return;
|
||||
}
|
||||
$counter = 1;
|
||||
foreach ($parts as $part) {
|
||||
$this->parts[$counter++] = new static(['headers' => $part['header'], 'content' => $part['body']]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get part of multipart message
|
||||
*
|
||||
* @param int $num number of part starting with 1 for first part
|
||||
* @throws Exception\RuntimeException
|
||||
* @return Part wanted part
|
||||
*/
|
||||
public function getPart($num)
|
||||
{
|
||||
if (isset($this->parts[$num])) {
|
||||
return $this->parts[$num];
|
||||
}
|
||||
|
||||
if (! $this->mail && $this->content === null) {
|
||||
throw new Exception\RuntimeException('part not found');
|
||||
}
|
||||
|
||||
if ($this->mail && $this->mail->hasFetchPart) {
|
||||
// TODO: fetch part
|
||||
// return
|
||||
}
|
||||
|
||||
$this->cacheContent();
|
||||
|
||||
if (! isset($this->parts[$num])) {
|
||||
throw new Exception\RuntimeException('part not found');
|
||||
}
|
||||
|
||||
return $this->parts[$num];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count parts of a multipart part
|
||||
*
|
||||
* @return int number of sub-parts
|
||||
*/
|
||||
public function countParts()
|
||||
{
|
||||
if ($this->countParts) {
|
||||
return $this->countParts;
|
||||
}
|
||||
|
||||
$this->countParts = count($this->parts);
|
||||
if ($this->countParts) {
|
||||
return $this->countParts;
|
||||
}
|
||||
|
||||
if ($this->mail && $this->mail->hasFetchPart) {
|
||||
// TODO: fetch part
|
||||
// return
|
||||
}
|
||||
|
||||
$this->cacheContent();
|
||||
|
||||
$this->countParts = count($this->parts);
|
||||
return $this->countParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access headers collection
|
||||
*
|
||||
* Lazy-loads if not already attached.
|
||||
*
|
||||
* @return Headers
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
if (null === $this->headers) {
|
||||
if ($this->mail) {
|
||||
$part = $this->mail->getRawHeader($this->messageNum);
|
||||
$this->headers = Headers::fromString($part);
|
||||
} else {
|
||||
$this->headers = new Headers();
|
||||
}
|
||||
}
|
||||
if (! $this->headers instanceof Headers) {
|
||||
throw new Exception\RuntimeException(
|
||||
'$this->headers must be an instance of Headers'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a header in specified format
|
||||
*
|
||||
* Internally headers that occur more than once are saved as array, all other as string. If $format
|
||||
* is set to string implode is used to concat the values (with Mime::LINEEND as delim).
|
||||
*
|
||||
* @param string $name name of header, matches case-insensitive, but camel-case is replaced with dashes
|
||||
* @param string $format change type of return value to 'string' or 'array'
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return string|array|HeaderInterface|\ArrayIterator value of header in wanted or internal format
|
||||
*/
|
||||
public function getHeader($name, $format = null)
|
||||
{
|
||||
$header = $this->getHeaders()->get($name);
|
||||
if ($header === false) {
|
||||
$lowerName = strtolower(preg_replace('%([a-z])([A-Z])%', '\1-\2', $name));
|
||||
$header = $this->getHeaders()->get($lowerName);
|
||||
if ($header === false) {
|
||||
throw new Exception\InvalidArgumentException(
|
||||
"Header with Name $name or $lowerName not found"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($format) {
|
||||
case 'string':
|
||||
if ($header instanceof HeaderInterface) {
|
||||
$return = $header->getFieldValue(HeaderInterface::FORMAT_RAW);
|
||||
} else {
|
||||
$return = '';
|
||||
foreach ($header as $h) {
|
||||
$return .= $h->getFieldValue(HeaderInterface::FORMAT_RAW)
|
||||
. Mime\Mime::LINEEND;
|
||||
}
|
||||
$return = trim($return, Mime\Mime::LINEEND);
|
||||
}
|
||||
break;
|
||||
case 'array':
|
||||
if ($header instanceof HeaderInterface) {
|
||||
$return = [$header->getFieldValue()];
|
||||
} else {
|
||||
$return = [];
|
||||
foreach ($header as $h) {
|
||||
$return[] = $h->getFieldValue(HeaderInterface::FORMAT_RAW);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$return = $header;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific field from a header like content type or all fields as array
|
||||
*
|
||||
* If the header occurs more than once, only the value from the first header
|
||||
* is returned.
|
||||
*
|
||||
* Throws an Exception if the requested header does not exist. If
|
||||
* the specific header field does not exist, returns null.
|
||||
*
|
||||
* @param string $name name of header, like in getHeader()
|
||||
* @param string $wantedPart the wanted part, default is first, if null an array with all parts is returned
|
||||
* @param string $firstName key name for the first part
|
||||
* @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
|
||||
* @throws \Laminas\Mime\Exception\RuntimeException
|
||||
*/
|
||||
public function getHeaderField($name, $wantedPart = '0', $firstName = '0')
|
||||
{
|
||||
return Mime\Decode::splitHeaderField(current($this->getHeader($name, 'array')), $wantedPart, $firstName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for mail headers - name is matched in lowercase
|
||||
*
|
||||
* This getter is short for Part::getHeader($name, 'string')
|
||||
*
|
||||
* @see Part::getHeader()
|
||||
*
|
||||
* @param string $name header name
|
||||
* @return string value of header
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->getHeader($name, 'string');
|
||||
}
|
||||
|
||||
/**
|
||||
* Isset magic method proxy to hasHeader
|
||||
*
|
||||
* This method is short syntax for Part::hasHeader($name);
|
||||
*
|
||||
* @see Part::hasHeader
|
||||
*
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return $this->getHeaders()->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* magic method to get content of part
|
||||
*
|
||||
* @return string content
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* implements RecursiveIterator::hasChildren()
|
||||
*
|
||||
* @return bool current element has children/is multipart
|
||||
*/
|
||||
public function hasChildren()
|
||||
{
|
||||
$current = $this->current();
|
||||
return $current && $current instanceof Part && $current->isMultipart();
|
||||
}
|
||||
|
||||
/**
|
||||
* implements RecursiveIterator::getChildren()
|
||||
*
|
||||
* @return Part same as self::current()
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::valid()
|
||||
*
|
||||
* @return bool check if there's a current element
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
if ($this->countParts === null) {
|
||||
$this->countParts();
|
||||
}
|
||||
return $this->iterationPos && $this->iterationPos <= $this->countParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::next()
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
++$this->iterationPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::key()
|
||||
*
|
||||
* @return string key/number of current part
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->iterationPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::current()
|
||||
*
|
||||
* @return Part current part
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->getPart($this->iterationPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* implements Iterator::rewind()
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->countParts();
|
||||
$this->iterationPos = 1;
|
||||
}
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Storage/Part/Exception/ExceptionInterface.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Storage/Part/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Part\Exception;
|
||||
|
||||
use Laminas\Mail\Storage\Exception\ExceptionInterface as StorageException;
|
||||
|
||||
interface ExceptionInterface extends StorageException
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Storage/Part/Exception/InvalidArgumentException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Storage/Part/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Part\Exception;
|
||||
|
||||
use Laminas\Mail\Storage\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class InvalidArgumentException extends Exception\InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Storage/Part/Exception/RuntimeException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Storage/Part/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Part\Exception;
|
||||
|
||||
use Laminas\Mail\Storage\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class RuntimeException extends Exception\RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
157
vendor/laminas/laminas-mail/src/Storage/Part/File.php
vendored
Normal file
157
vendor/laminas/laminas-mail/src/Storage/Part/File.php
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Part;
|
||||
|
||||
use Laminas\Mail\Headers;
|
||||
use Laminas\Mail\Storage\Part;
|
||||
|
||||
class File extends Part
|
||||
{
|
||||
protected $contentPos = [];
|
||||
protected $partPos = [];
|
||||
protected $fh;
|
||||
|
||||
/**
|
||||
* Public constructor
|
||||
*
|
||||
* This handler supports the following params:
|
||||
* - file filename or open file handler with message content (required)
|
||||
* - startPos start position of message or part in file (default: current position)
|
||||
* - endPos end position of message or part in file (default: end of file)
|
||||
* - EOL end of Line for messages
|
||||
*
|
||||
* @param array $params full message with or without headers
|
||||
* @throws Exception\RuntimeException
|
||||
* @throws Exception\InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $params)
|
||||
{
|
||||
if (empty($params['file'])) {
|
||||
throw new Exception\InvalidArgumentException('no file given in params');
|
||||
}
|
||||
|
||||
if (! is_resource($params['file'])) {
|
||||
$this->fh = fopen($params['file'], 'r');
|
||||
} else {
|
||||
$this->fh = $params['file'];
|
||||
}
|
||||
if (! $this->fh) {
|
||||
throw new Exception\RuntimeException('could not open file');
|
||||
}
|
||||
if (isset($params['startPos'])) {
|
||||
fseek($this->fh, $params['startPos']);
|
||||
}
|
||||
$header = '';
|
||||
$endPos = isset($params['endPos']) ? $params['endPos'] : null;
|
||||
while (($endPos === null || ftell($this->fh) < $endPos) && trim($line = fgets($this->fh))) {
|
||||
$header .= $line;
|
||||
}
|
||||
|
||||
if (isset($params['EOL'])) {
|
||||
$this->headers = Headers::fromString($header, $params['EOL']);
|
||||
} else {
|
||||
$this->headers = Headers::fromString($header);
|
||||
}
|
||||
|
||||
$this->contentPos[0] = ftell($this->fh);
|
||||
if ($endPos !== null) {
|
||||
$this->contentPos[1] = $endPos;
|
||||
} else {
|
||||
fseek($this->fh, 0, SEEK_END);
|
||||
$this->contentPos[1] = ftell($this->fh);
|
||||
}
|
||||
if (! $this->isMultipart()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$boundary = $this->getHeaderField('content-type', 'boundary');
|
||||
if (! $boundary) {
|
||||
throw new Exception\RuntimeException('no boundary found in content type to split message');
|
||||
}
|
||||
|
||||
$part = [];
|
||||
$pos = $this->contentPos[0];
|
||||
fseek($this->fh, $pos);
|
||||
while (! feof($this->fh) && ($endPos === null || $pos < $endPos)) {
|
||||
$line = fgets($this->fh);
|
||||
if ($line === false) {
|
||||
if (feof($this->fh)) {
|
||||
break;
|
||||
}
|
||||
throw new Exception\RuntimeException('error reading file');
|
||||
}
|
||||
|
||||
$lastPos = $pos;
|
||||
$pos = ftell($this->fh);
|
||||
$line = trim($line);
|
||||
|
||||
if ($line == '--' . $boundary) {
|
||||
if ($part) {
|
||||
// not first part
|
||||
$part[1] = $lastPos;
|
||||
$this->partPos[] = $part;
|
||||
}
|
||||
$part = [$pos];
|
||||
} elseif ($line == '--' . $boundary . '--') {
|
||||
$part[1] = $lastPos;
|
||||
$this->partPos[] = $part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->countParts = count($this->partPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Body of part
|
||||
*
|
||||
* If part is multipart the raw content of this part with all sub parts is returned
|
||||
*
|
||||
* @param resource $stream Optional
|
||||
* @return string body
|
||||
*/
|
||||
public function getContent($stream = null)
|
||||
{
|
||||
fseek($this->fh, $this->contentPos[0]);
|
||||
if ($stream !== null) {
|
||||
return stream_copy_to_stream($this->fh, $stream, $this->contentPos[1] - $this->contentPos[0]);
|
||||
}
|
||||
$length = $this->contentPos[1] - $this->contentPos[0];
|
||||
return $length < 1 ? '' : fread($this->fh, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return size of part
|
||||
*
|
||||
* Quite simple implemented currently (not decoding). Handle with care.
|
||||
*
|
||||
* @return int size
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return $this->contentPos[1] - $this->contentPos[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get part of multipart message
|
||||
*
|
||||
* @param int $num number of part starting with 1 for first part
|
||||
* @throws Exception\RuntimeException
|
||||
* @return Part wanted part
|
||||
*/
|
||||
public function getPart($num)
|
||||
{
|
||||
--$num;
|
||||
if (! isset($this->partPos[$num])) {
|
||||
throw new Exception\RuntimeException('part not found');
|
||||
}
|
||||
|
||||
return new static(['file' => $this->fh, 'startPos' => $this->partPos[$num][0],
|
||||
'endPos' => $this->partPos[$num][1]]);
|
||||
}
|
||||
}
|
||||
123
vendor/laminas/laminas-mail/src/Storage/Part/PartInterface.php
vendored
Normal file
123
vendor/laminas/laminas-mail/src/Storage/Part/PartInterface.php
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Part;
|
||||
|
||||
use ArrayIterator;
|
||||
use Laminas\Mail\Header\HeaderInterface;
|
||||
use Laminas\Mail\Headers;
|
||||
use RecursiveIterator;
|
||||
|
||||
interface PartInterface extends RecursiveIterator
|
||||
{
|
||||
/**
|
||||
* Check if part is a multipart message
|
||||
*
|
||||
* @return bool if part is multipart
|
||||
*/
|
||||
public function isMultipart();
|
||||
|
||||
/**
|
||||
* Body of part
|
||||
*
|
||||
* If part is multipart the raw content of this part with all sub parts is
|
||||
* returned.
|
||||
*
|
||||
* @return string body
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
public function getContent();
|
||||
|
||||
/**
|
||||
* Return size of part
|
||||
*
|
||||
* @return int size
|
||||
*/
|
||||
public function getSize();
|
||||
|
||||
/**
|
||||
* Get part of multipart message
|
||||
*
|
||||
* @param int $num number of part starting with 1 for first part
|
||||
* @return PartInterface wanted part
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
public function getPart($num);
|
||||
|
||||
/**
|
||||
* Count parts of a multipart part
|
||||
*
|
||||
* @return int number of sub-parts
|
||||
*/
|
||||
public function countParts();
|
||||
|
||||
/**
|
||||
* Get all headers
|
||||
*
|
||||
* The returned headers are as saved internally. All names are lowercased.
|
||||
* The value is a string or an array if a header with the same name occurs
|
||||
* more than once.
|
||||
*
|
||||
* @return Headers
|
||||
*/
|
||||
public function getHeaders();
|
||||
|
||||
/**
|
||||
* Get a header in specified format
|
||||
*
|
||||
* Internally headers that occur more than once are saved as array, all
|
||||
* other as string. If $format is set to string implode is used to concat
|
||||
* the values (with Laminas\Mime\Mime::LINEEND as delim).
|
||||
*
|
||||
* @param string $name name of header, matches case-insensitive, but
|
||||
* camel-case is replaced with dashes
|
||||
* @param string $format change type of return value to 'string' or 'array'
|
||||
* @return string|array|HeaderInterface|ArrayIterator value of header in
|
||||
* wanted or internal format
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
public function getHeader($name, $format = null);
|
||||
|
||||
/**
|
||||
* Get a specific field from a header like content type or all fields as array
|
||||
*
|
||||
* If the header occurs more than once, only the value from the first
|
||||
* header is returned.
|
||||
*
|
||||
* Throws an exception if the requested header does not exist. If the
|
||||
* specific header field does not exist, returns null.
|
||||
*
|
||||
* @param string $name name of header, like in getHeader()
|
||||
* @param string $wantedPart the wanted part, default is first, if null an
|
||||
* array with all parts is returned
|
||||
* @param string $firstName key name for the first part
|
||||
* @return string|array wanted part or all parts as
|
||||
* [$firstName => firstPart, partname => value]
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
public function getHeaderField($name, $wantedPart = '0', $firstName = '0');
|
||||
|
||||
/**
|
||||
* Getter for mail headers - name is matched in lowercase
|
||||
*
|
||||
* This getter is short for PartInterface::getHeader($name, 'string')
|
||||
*
|
||||
* @see PartInterface::getHeader()
|
||||
* @param string $name header name
|
||||
* @return string value of header
|
||||
* @throws Exception\ExceptionInterface
|
||||
*/
|
||||
public function __get($name);
|
||||
|
||||
/**
|
||||
* magic method to get content of part
|
||||
*
|
||||
* @return string content
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
||||
283
vendor/laminas/laminas-mail/src/Storage/Pop3.php
vendored
Normal file
283
vendor/laminas/laminas-mail/src/Storage/Pop3.php
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage;
|
||||
|
||||
use Laminas\Mail\Exception as MailException;
|
||||
use Laminas\Mail\Protocol;
|
||||
use Laminas\Mime;
|
||||
|
||||
class Pop3 extends AbstractStorage
|
||||
{
|
||||
/**
|
||||
* protocol handler
|
||||
* @var null|\Laminas\Mail\Protocol\Pop3
|
||||
*/
|
||||
protected $protocol;
|
||||
|
||||
/**
|
||||
* Count messages all messages in current box
|
||||
*
|
||||
* @return int number of messages
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function countMessages()
|
||||
{
|
||||
$count = 0; // "Declare" variable before first usage.
|
||||
$octets = 0; // "Declare" variable since it's passed by reference
|
||||
$this->protocol->status($count, $octets);
|
||||
return (int) $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a list of messages with number and size
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @return int|array size of given message of list with all messages as array(num => size)
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getSize($id = 0)
|
||||
{
|
||||
$id = $id ? $id : null;
|
||||
return $this->protocol->getList($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @return \Laminas\Mail\Storage\Message
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getMessage($id)
|
||||
{
|
||||
$bodyLines = 0;
|
||||
$message = $this->protocol->top($id, $bodyLines, true);
|
||||
|
||||
return new $this->messageClass(['handler' => $this, 'id' => $id, 'headers' => $message,
|
||||
'noToplines' => $bodyLines < 1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw header of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message header
|
||||
* @param int $topLines include this many lines with header (after an empty line)
|
||||
* @return string raw header
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getRawHeader($id, $part = null, $topLines = 0)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
|
||||
return $this->protocol->top($id, 0, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get raw content of message or part
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param null|array|string $part path to part or null for message content
|
||||
* @return string raw content
|
||||
* @throws \Laminas\Mail\Protocol\Exception\ExceptionInterface
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getRawContent($id, $part = null)
|
||||
{
|
||||
if ($part !== null) {
|
||||
// TODO: implement
|
||||
throw new Exception\RuntimeException('not implemented');
|
||||
}
|
||||
|
||||
$content = $this->protocol->retrieve($id);
|
||||
// TODO: find a way to avoid decoding the headers
|
||||
$headers = null; // "Declare" variable since it's passed by reference
|
||||
$body = null; // "Declare" variable before first usage.
|
||||
Mime\Decode::splitMessage($content, $headers, $body);
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* create instance with parameters
|
||||
* Supported parameters are
|
||||
* - host hostname or ip address of POP3 server
|
||||
* - user username
|
||||
* - password password for user 'username' [optional, default = '']
|
||||
* - port port for POP3 server [optional, default = 110]
|
||||
* - ssl 'SSL' or 'TLS' for secure sockets
|
||||
*
|
||||
* @param array|Protocol\Pop3 $params mail reader specific parameters or configured Pop3 protocol object
|
||||
* @throws \Laminas\Mail\Storage\Exception\InvalidArgumentException
|
||||
* @throws \Laminas\Mail\Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
$params = (object) $params;
|
||||
}
|
||||
|
||||
$this->has['fetchPart'] = false;
|
||||
$this->has['top'] = null;
|
||||
$this->has['uniqueid'] = null;
|
||||
|
||||
if ($params instanceof Protocol\Pop3) {
|
||||
$this->protocol = $params;
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset($params->user)) {
|
||||
throw new Exception\InvalidArgumentException('need at least user in params');
|
||||
}
|
||||
|
||||
$host = isset($params->host) ? $params->host : 'localhost';
|
||||
$password = isset($params->password) ? $params->password : '';
|
||||
$port = isset($params->port) ? $params->port : null;
|
||||
$ssl = isset($params->ssl) ? $params->ssl : false;
|
||||
|
||||
$this->protocol = new Protocol\Pop3();
|
||||
|
||||
if (isset($params->novalidatecert)) {
|
||||
$this->protocol->setNoValidateCert((bool)$params->novalidatecert);
|
||||
}
|
||||
|
||||
$this->protocol->connect($host, $port, $ssl);
|
||||
$this->protocol->login($params->user, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close resource for mail lib. If you need to control, when the resource
|
||||
* is closed. Otherwise the destructor would call this.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->protocol->logout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the server busy.
|
||||
*
|
||||
* @throws \Laminas\Mail\Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function noop()
|
||||
{
|
||||
$this->protocol->noop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a message from server. If you're doing that from a web environment
|
||||
* you should be careful and use a uniqueid as parameter if possible to
|
||||
* identify the message.
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @throws \Laminas\Mail\Protocol\Exception\RuntimeException
|
||||
*/
|
||||
public function removeMessage($id)
|
||||
{
|
||||
$this->protocol->delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* get unique id for one or all messages
|
||||
*
|
||||
* if storage does not support unique ids it's the same as the message number
|
||||
*
|
||||
* @param int|null $id message number
|
||||
* @return array|string message number for given message or all messages as array
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function getUniqueId($id = null)
|
||||
{
|
||||
if (! $this->hasUniqueid) {
|
||||
if ($id) {
|
||||
return $id;
|
||||
}
|
||||
$count = $this->countMessages();
|
||||
if ($count < 1) {
|
||||
return [];
|
||||
}
|
||||
$range = range(1, $count);
|
||||
return array_combine($range, $range);
|
||||
}
|
||||
|
||||
return $this->protocol->uniqueid($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a message number from a unique id
|
||||
*
|
||||
* I.e. if you have a webmailer that supports deleting messages you should use unique ids
|
||||
* as parameter and use this method to translate it to message number right before calling removeMessage()
|
||||
*
|
||||
* @param string $id unique id
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return int message number
|
||||
*/
|
||||
public function getNumberByUniqueId($id)
|
||||
{
|
||||
if (! $this->hasUniqueid) {
|
||||
return $id;
|
||||
}
|
||||
|
||||
$ids = $this->getUniqueId();
|
||||
foreach ($ids as $k => $v) {
|
||||
if ($v == $id) {
|
||||
return $k;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception\InvalidArgumentException('unique id not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Special handling for hasTop and hasUniqueid. The headers of the first message is
|
||||
* retrieved if Top wasn't needed/tried yet.
|
||||
*
|
||||
* @see AbstractStorage::__get()
|
||||
* @param string $var
|
||||
* @return string
|
||||
*/
|
||||
public function __get($var)
|
||||
{
|
||||
$result = parent::__get($var);
|
||||
if ($result !== null) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (strtolower($var) == 'hastop') {
|
||||
if ($this->protocol->hasTop === null) {
|
||||
// need to make a real call, because not all server are honest in their capas
|
||||
try {
|
||||
$this->protocol->top(1, 0, false);
|
||||
} catch (MailException\ExceptionInterface $e) {
|
||||
// ignoring error
|
||||
}
|
||||
}
|
||||
$this->has['top'] = $this->protocol->hasTop;
|
||||
return $this->protocol->hasTop;
|
||||
}
|
||||
|
||||
if (strtolower($var) == 'hasuniqueid') {
|
||||
$id = null;
|
||||
try {
|
||||
$id = $this->protocol->uniqueid(1);
|
||||
} catch (MailException\ExceptionInterface $e) {
|
||||
// ignoring error
|
||||
}
|
||||
$this->has['uniqueid'] = (bool) $id;
|
||||
return $this->has['uniqueid'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
953
vendor/laminas/laminas-mail/src/Storage/Writable/Maildir.php
vendored
Normal file
953
vendor/laminas/laminas-mail/src/Storage/Writable/Maildir.php
vendored
Normal file
@ -0,0 +1,953 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Writable;
|
||||
|
||||
use Laminas\Mail\Exception as MailException;
|
||||
use Laminas\Mail\Storage;
|
||||
use Laminas\Mail\Storage\Exception as StorageException;
|
||||
use Laminas\Mail\Storage\Folder;
|
||||
use Laminas\Stdlib\ErrorHandler;
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
class Maildir extends Folder\Maildir implements WritableInterface
|
||||
{
|
||||
// TODO: init maildir (+ constructor option create if not found)
|
||||
|
||||
/**
|
||||
* use quota and size of quota if given
|
||||
*
|
||||
* @var bool|int
|
||||
*/
|
||||
protected $quota;
|
||||
|
||||
/**
|
||||
* create a new maildir
|
||||
*
|
||||
* If the given dir is already a valid maildir this will not fail.
|
||||
*
|
||||
* @param string $dir directory for the new maildir (may already exist)
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
* @throws \Laminas\Mail\Storage\Exception\InvalidArgumentException
|
||||
*/
|
||||
public static function initMaildir($dir)
|
||||
{
|
||||
if (file_exists($dir)) {
|
||||
if (! is_dir($dir)) {
|
||||
throw new StorageException\InvalidArgumentException('maildir must be a directory if already exists');
|
||||
}
|
||||
} else {
|
||||
ErrorHandler::start();
|
||||
$test = mkdir($dir);
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $test) {
|
||||
$dir = dirname($dir);
|
||||
if (! file_exists($dir)) {
|
||||
throw new StorageException\InvalidArgumentException("parent $dir not found", 0, $error);
|
||||
} elseif (! is_dir($dir)) {
|
||||
throw new StorageException\InvalidArgumentException("parent $dir not a directory", 0, $error);
|
||||
} else {
|
||||
throw new StorageException\RuntimeException('cannot create maildir', 0, $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (['cur', 'tmp', 'new'] as $subdir) {
|
||||
ErrorHandler::start();
|
||||
$test = mkdir($dir . DIRECTORY_SEPARATOR . $subdir);
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $test) {
|
||||
// ignore if dir exists (i.e. was already valid maildir or two processes try to create one)
|
||||
if (! file_exists($dir . DIRECTORY_SEPARATOR . $subdir)) {
|
||||
throw new StorageException\RuntimeException('could not create subdir ' . $subdir, 0, $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create instance with parameters
|
||||
* Additional parameters are (see parent for more):
|
||||
* - create if true a new maildir is create if none exists
|
||||
*
|
||||
* @param $params array mail reader specific parameters
|
||||
* @throws \Laminas\Mail\Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
$params = (object) $params;
|
||||
}
|
||||
|
||||
if (! empty($params->create)
|
||||
&& isset($params->dirname)
|
||||
&& ! file_exists($params->dirname . DIRECTORY_SEPARATOR . 'cur')
|
||||
) {
|
||||
self::initMaildir($params->dirname);
|
||||
}
|
||||
|
||||
parent::__construct($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new folder
|
||||
*
|
||||
* This method also creates parent folders if necessary. Some mail storages may restrict, which folder
|
||||
* may be used as parent or which chars may be used in the folder name
|
||||
*
|
||||
* @param string $name global name of folder, local name if $parentFolder is set
|
||||
* @param string|\Laminas\Mail\Storage\Folder $parentFolder parent of new folder, else root folder is parent
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
* @return string only used internally (new created maildir)
|
||||
*/
|
||||
public function createFolder($name, $parentFolder = null)
|
||||
{
|
||||
if ($parentFolder instanceof Folder) {
|
||||
$folder = $parentFolder->getGlobalName() . $this->delim . $name;
|
||||
} elseif ($parentFolder !== null) {
|
||||
$folder = rtrim($parentFolder, $this->delim) . $this->delim . $name;
|
||||
} else {
|
||||
$folder = $name;
|
||||
}
|
||||
|
||||
$folder = trim($folder, $this->delim);
|
||||
|
||||
// first we check if we try to create a folder that does exist
|
||||
$exists = null;
|
||||
try {
|
||||
$exists = $this->getFolders($folder);
|
||||
} catch (MailException\ExceptionInterface $e) {
|
||||
// ok
|
||||
}
|
||||
if ($exists) {
|
||||
throw new StorageException\RuntimeException('folder already exists');
|
||||
}
|
||||
|
||||
if (strpos($folder, $this->delim . $this->delim) !== false) {
|
||||
throw new StorageException\RuntimeException('invalid name - folder parts may not be empty');
|
||||
}
|
||||
|
||||
if (strpos($folder, 'INBOX' . $this->delim) === 0) {
|
||||
$folder = substr($folder, 6);
|
||||
}
|
||||
|
||||
$fulldir = $this->rootdir . '.' . $folder;
|
||||
|
||||
// check if we got tricked and would create a dir outside of the rootdir or not as direct child
|
||||
if (strpos($folder, DIRECTORY_SEPARATOR) !== false || strpos($folder, '/') !== false
|
||||
|| dirname($fulldir) . DIRECTORY_SEPARATOR != $this->rootdir
|
||||
) {
|
||||
throw new StorageException\RuntimeException('invalid name - no directory separator allowed in folder name');
|
||||
}
|
||||
|
||||
// has a parent folder?
|
||||
$parent = null;
|
||||
if (strpos($folder, $this->delim)) {
|
||||
// let's see if the parent folder exists
|
||||
$parent = substr($folder, 0, strrpos($folder, $this->delim));
|
||||
try {
|
||||
$this->getFolders($parent);
|
||||
} catch (MailException\ExceptionInterface $e) {
|
||||
// does not - create parent folder
|
||||
$this->createFolder($parent);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorHandler::start();
|
||||
if (! mkdir($fulldir) || ! mkdir($fulldir . DIRECTORY_SEPARATOR . 'cur')) {
|
||||
$error = ErrorHandler::stop();
|
||||
throw new StorageException\RuntimeException(
|
||||
'error while creating new folder, may be created incompletely',
|
||||
0,
|
||||
$error
|
||||
);
|
||||
}
|
||||
ErrorHandler::stop();
|
||||
|
||||
mkdir($fulldir . DIRECTORY_SEPARATOR . 'new');
|
||||
mkdir($fulldir . DIRECTORY_SEPARATOR . 'tmp');
|
||||
|
||||
$localName = $parent ? substr($folder, strlen($parent) + 1) : $folder;
|
||||
$this->getFolders($parent)->$localName = new Folder($localName, $folder, true);
|
||||
|
||||
return $fulldir;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a folder
|
||||
*
|
||||
* @param string|Folder $name name or instance of folder
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
*/
|
||||
public function removeFolder($name)
|
||||
{
|
||||
// TODO: This could fail in the middle of the task, which is not optimal.
|
||||
// But there is no defined standard way to mark a folder as removed and there is no atomar fs-op
|
||||
// to remove a directory. Also moving the folder to a/the trash folder is not possible, as
|
||||
// all parent folders must be created. What we could do is add a dash to the front of the
|
||||
// directory name and it should be ignored as long as other processes obey the standard.
|
||||
|
||||
if ($name instanceof Folder) {
|
||||
$name = $name->getGlobalName();
|
||||
}
|
||||
|
||||
$name = trim($name, $this->delim);
|
||||
if (strpos($name, 'INBOX' . $this->delim) === 0) {
|
||||
$name = substr($name, 6);
|
||||
}
|
||||
|
||||
// check if folder exists and has no children
|
||||
if (! $this->getFolders($name)->isLeaf()) {
|
||||
throw new StorageException\RuntimeException('delete children first');
|
||||
}
|
||||
|
||||
if ($name == 'INBOX' || $name == DIRECTORY_SEPARATOR || $name == '/') {
|
||||
throw new StorageException\RuntimeException('wont delete INBOX');
|
||||
}
|
||||
|
||||
if ($name == $this->getCurrentFolder()) {
|
||||
throw new StorageException\RuntimeException('wont delete selected folder');
|
||||
}
|
||||
|
||||
foreach (['tmp', 'new', 'cur', '.'] as $subdir) {
|
||||
$dir = $this->rootdir . '.' . $name . DIRECTORY_SEPARATOR . $subdir;
|
||||
if (! file_exists($dir)) {
|
||||
continue;
|
||||
}
|
||||
$dh = opendir($dir);
|
||||
if (! $dh) {
|
||||
throw new StorageException\RuntimeException("error opening $subdir");
|
||||
}
|
||||
while (($entry = readdir($dh)) !== false) {
|
||||
if ($entry == '.' || $entry == '..') {
|
||||
continue;
|
||||
}
|
||||
if (! unlink($dir . DIRECTORY_SEPARATOR . $entry)) {
|
||||
throw new StorageException\RuntimeException("error cleaning $subdir");
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
if ($subdir !== '.') {
|
||||
if (! rmdir($dir)) {
|
||||
throw new StorageException\RuntimeException("error removing $subdir");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! rmdir($this->rootdir . '.' . $name)) {
|
||||
// at least we should try to make it a valid maildir again
|
||||
mkdir($this->rootdir . '.' . $name . DIRECTORY_SEPARATOR . 'cur');
|
||||
throw new StorageException\RuntimeException("error removing maindir");
|
||||
}
|
||||
|
||||
$parent = strpos($name, $this->delim) ? substr($name, 0, strrpos($name, $this->delim)) : null;
|
||||
$localName = $parent ? substr($name, strlen($parent) + 1) : $name;
|
||||
unset($this->getFolders($parent)->$localName);
|
||||
}
|
||||
|
||||
/**
|
||||
* rename and/or move folder
|
||||
*
|
||||
* The new name has the same restrictions as in createFolder()
|
||||
*
|
||||
* @param string|\Laminas\Mail\Storage\Folder $oldName name or instance of folder
|
||||
* @param string $newName new global name of folder
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
*/
|
||||
public function renameFolder($oldName, $newName)
|
||||
{
|
||||
// TODO: This is also not atomar and has similar problems as removeFolder()
|
||||
|
||||
if ($oldName instanceof Folder) {
|
||||
$oldName = $oldName->getGlobalName();
|
||||
}
|
||||
|
||||
$oldName = trim($oldName, $this->delim);
|
||||
if (strpos($oldName, 'INBOX' . $this->delim) === 0) {
|
||||
$oldName = substr($oldName, 6);
|
||||
}
|
||||
|
||||
$newName = trim($newName, $this->delim);
|
||||
if (strpos($newName, 'INBOX' . $this->delim) === 0) {
|
||||
$newName = substr($newName, 6);
|
||||
}
|
||||
|
||||
if (strpos($newName, $oldName . $this->delim) === 0) {
|
||||
throw new StorageException\RuntimeException('new folder cannot be a child of old folder');
|
||||
}
|
||||
|
||||
// check if folder exists and has no children
|
||||
$folder = $this->getFolders($oldName);
|
||||
|
||||
if ($oldName == 'INBOX' || $oldName == DIRECTORY_SEPARATOR || $oldName == '/') {
|
||||
throw new StorageException\RuntimeException('wont rename INBOX');
|
||||
}
|
||||
|
||||
if ($oldName == $this->getCurrentFolder()) {
|
||||
throw new StorageException\RuntimeException('wont rename selected folder');
|
||||
}
|
||||
|
||||
$newdir = $this->createFolder($newName);
|
||||
|
||||
if (! $folder->isLeaf()) {
|
||||
foreach ($folder as $k => $v) {
|
||||
$this->renameFolder($v->getGlobalName(), $newName . $this->delim . $k);
|
||||
}
|
||||
}
|
||||
|
||||
$olddir = $this->rootdir . '.' . $folder;
|
||||
foreach (['tmp', 'new', 'cur'] as $subdir) {
|
||||
$subdir = DIRECTORY_SEPARATOR . $subdir;
|
||||
if (! file_exists($olddir . $subdir)) {
|
||||
continue;
|
||||
}
|
||||
// using copy or moving files would be even better - but also much slower
|
||||
if (! rename($olddir . $subdir, $newdir . $subdir)) {
|
||||
throw new StorageException\RuntimeException('error while moving ' . $subdir);
|
||||
}
|
||||
}
|
||||
// create a dummy if removing fails - otherwise we can't read it next time
|
||||
mkdir($olddir . DIRECTORY_SEPARATOR . 'cur');
|
||||
$this->removeFolder($oldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a uniqueid for maildir filename
|
||||
*
|
||||
* This is nearly the format defined in the maildir standard. The microtime() call should already
|
||||
* create a uniqueid, the pid is for multicore/-cpu machine that manage to call this function at the
|
||||
* exact same time, and uname() gives us the hostname for multiple machines accessing the same storage.
|
||||
*
|
||||
* If someone disables posix we create a random number of the same size, so this method should also
|
||||
* work on Windows - if you manage to get maildir working on Windows.
|
||||
* Microtime could also be disabled, although I've never seen it.
|
||||
*
|
||||
* @return string new uniqueid
|
||||
*/
|
||||
protected function createUniqueId()
|
||||
{
|
||||
$id = '';
|
||||
$id .= microtime(true);
|
||||
$id .= '.' . getmypid();
|
||||
$id .= '.' . php_uname('n');
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* open a temporary maildir file
|
||||
*
|
||||
* makes sure tmp/ exists and create a file with a unique name
|
||||
* you should close the returned filehandle!
|
||||
*
|
||||
* @param string $folder name of current folder without leading .
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
* @return array array('dirname' => dir of maildir folder, 'uniq' => unique id, 'filename' => name of create file
|
||||
* 'handle' => file opened for writing)
|
||||
*/
|
||||
protected function createTmpFile($folder = 'INBOX')
|
||||
{
|
||||
if ($folder == 'INBOX') {
|
||||
$tmpdir = $this->rootdir . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
} else {
|
||||
$tmpdir = $this->rootdir . '.' . $folder . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
if (! file_exists($tmpdir)) {
|
||||
if (! mkdir($tmpdir)) {
|
||||
throw new StorageException\RuntimeException('problems creating tmp dir');
|
||||
}
|
||||
}
|
||||
|
||||
// we should retry to create a unique id if a file with the same name exists
|
||||
// to avoid a script timeout we only wait 1 second (instead of 2) and stop
|
||||
// after a defined retry count
|
||||
// if you change this variable take into account that it can take up to $maxTries seconds
|
||||
// normally we should have a valid unique name after the first try, we're just following the "standard" here
|
||||
$maxTries = 5;
|
||||
for ($i = 0; $i < $maxTries; ++$i) {
|
||||
$uniq = $this->createUniqueId();
|
||||
if (! file_exists($tmpdir . $uniq)) {
|
||||
// here is the race condition! - as defined in the standard
|
||||
// to avoid having a long time between stat()ing the file and creating it we're opening it here
|
||||
// to mark the filename as taken
|
||||
$fh = fopen($tmpdir . $uniq, 'w');
|
||||
if (! $fh) {
|
||||
throw new StorageException\RuntimeException('could not open temp file');
|
||||
}
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (! $fh) {
|
||||
throw new StorageException\RuntimeException(
|
||||
"tried {$maxTries} unique ids for a temp file, but all were taken - giving up"
|
||||
);
|
||||
}
|
||||
|
||||
return ['dirname' => $this->rootdir . '.' . $folder,
|
||||
'uniq' => $uniq,
|
||||
'filename' => $tmpdir . $uniq,
|
||||
'handle' => $fh];
|
||||
}
|
||||
|
||||
/**
|
||||
* create an info string for filenames with given flags
|
||||
*
|
||||
* @param array $flags wanted flags, with the reference you'll get the set
|
||||
* flags with correct key (= char for flag)
|
||||
* @return string info string for version 2 filenames including the leading colon
|
||||
* @throws StorageException\InvalidArgumentException
|
||||
*/
|
||||
protected function getInfoString(&$flags)
|
||||
{
|
||||
// accessing keys is easier, faster and it removes duplicated flags
|
||||
$wantedFlags = array_flip($flags);
|
||||
if (isset($wantedFlags[Storage::FLAG_RECENT])) {
|
||||
throw new StorageException\InvalidArgumentException('recent flag may not be set');
|
||||
}
|
||||
|
||||
$info = ':2,';
|
||||
$flags = [];
|
||||
foreach (Storage\Maildir::$knownFlags as $char => $flag) {
|
||||
if (! isset($wantedFlags[$flag])) {
|
||||
continue;
|
||||
}
|
||||
$info .= $char;
|
||||
$flags[$char] = $flag;
|
||||
unset($wantedFlags[$flag]);
|
||||
}
|
||||
|
||||
if (! empty($wantedFlags)) {
|
||||
$wantedFlags = implode(', ', array_keys($wantedFlags));
|
||||
throw new StorageException\InvalidArgumentException('unknown flag(s): ' . $wantedFlags);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* append a new message to mail storage
|
||||
*
|
||||
* @param string|resource $message message as string or stream resource.
|
||||
* @param null|string|Folder $folder folder for new message, else current
|
||||
* folder is taken.
|
||||
* @param null|array $flags set flags for new message, else a default set
|
||||
* is used.
|
||||
* @param bool $recent handle this mail as if recent flag has been set,
|
||||
* should only be used in delivery.
|
||||
* @throws StorageException\RuntimeException
|
||||
*/
|
||||
public function appendMessage($message, $folder = null, $flags = null, $recent = false)
|
||||
{
|
||||
if ($this->quota && $this->checkQuota()) {
|
||||
throw new StorageException\RuntimeException('storage is over quota!');
|
||||
}
|
||||
|
||||
if ($folder === null) {
|
||||
$folder = $this->currentFolder;
|
||||
}
|
||||
|
||||
if (! ($folder instanceof Folder)) {
|
||||
$folder = $this->getFolders($folder);
|
||||
}
|
||||
|
||||
if ($flags === null) {
|
||||
$flags = [Storage::FLAG_SEEN];
|
||||
}
|
||||
$info = $this->getInfoString($flags);
|
||||
$tempFile = $this->createTmpFile($folder->getGlobalName());
|
||||
|
||||
// TODO: handle class instances for $message
|
||||
if (is_resource($message) && get_resource_type($message) == 'stream') {
|
||||
stream_copy_to_stream($message, $tempFile['handle']);
|
||||
} else {
|
||||
fwrite($tempFile['handle'], $message);
|
||||
}
|
||||
fclose($tempFile['handle']);
|
||||
|
||||
// we're adding the size to the filename for maildir++
|
||||
$size = filesize($tempFile['filename']);
|
||||
if ($size !== false) {
|
||||
$info = ',S=' . $size . $info;
|
||||
}
|
||||
$newFilename = $tempFile['dirname'] . DIRECTORY_SEPARATOR;
|
||||
$newFilename .= $recent ? 'new' : 'cur';
|
||||
$newFilename .= DIRECTORY_SEPARATOR . $tempFile['uniq'] . $info;
|
||||
|
||||
// we're throwing any exception after removing our temp file and saving it to this variable instead
|
||||
$exception = null;
|
||||
|
||||
if (! link($tempFile['filename'], $newFilename)) {
|
||||
$exception = new StorageException\RuntimeException('cannot link message file to final dir');
|
||||
}
|
||||
|
||||
ErrorHandler::start(E_WARNING);
|
||||
unlink($tempFile['filename']);
|
||||
ErrorHandler::stop();
|
||||
|
||||
if ($exception) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$this->files[] = ['uniq' => $tempFile['uniq'],
|
||||
'flags' => $flags,
|
||||
'filename' => $newFilename];
|
||||
if ($this->quota) {
|
||||
$this->addQuotaEntry((int) $size, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* copy an existing message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param string|\Laminas\Mail\Storage\Folder $folder name or instance of targer folder
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
*/
|
||||
public function copyMessage($id, $folder)
|
||||
{
|
||||
if ($this->quota && $this->checkQuota()) {
|
||||
throw new StorageException\RuntimeException('storage is over quota!');
|
||||
}
|
||||
|
||||
if (! ($folder instanceof Folder)) {
|
||||
$folder = $this->getFolders($folder);
|
||||
}
|
||||
|
||||
$filedata = $this->getFileData($id);
|
||||
$oldFile = $filedata['filename'];
|
||||
$flags = $filedata['flags'];
|
||||
|
||||
// copied message can't be recent
|
||||
while (($key = array_search(Storage::FLAG_RECENT, $flags)) !== false) {
|
||||
unset($flags[$key]);
|
||||
}
|
||||
$info = $this->getInfoString($flags);
|
||||
|
||||
// we're creating the copy as temp file before moving to cur/
|
||||
$tempFile = $this->createTmpFile($folder->getGlobalName());
|
||||
// we don't write directly to the file
|
||||
fclose($tempFile['handle']);
|
||||
|
||||
// we're adding the size to the filename for maildir++
|
||||
$size = filesize($oldFile);
|
||||
if ($size !== false) {
|
||||
$info = ',S=' . $size . $info;
|
||||
}
|
||||
|
||||
$newFile = $tempFile['dirname'] . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . $tempFile['uniq'] . $info;
|
||||
|
||||
// we're throwing any exception after removing our temp file and saving it to this variable instead
|
||||
$exception = null;
|
||||
|
||||
if (! copy($oldFile, $tempFile['filename'])) {
|
||||
$exception = new StorageException\RuntimeException('cannot copy message file');
|
||||
} elseif (! link($tempFile['filename'], $newFile)) {
|
||||
$exception = new StorageException\RuntimeException('cannot link message file to final dir');
|
||||
}
|
||||
|
||||
ErrorHandler::start(E_WARNING);
|
||||
unlink($tempFile['filename']);
|
||||
ErrorHandler::stop();
|
||||
|
||||
if ($exception) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ($folder->getGlobalName() == $this->currentFolder
|
||||
|| ($this->currentFolder == 'INBOX' && $folder->getGlobalName() == '/')
|
||||
) {
|
||||
$this->files[] = ['uniq' => $tempFile['uniq'],
|
||||
'flags' => $flags,
|
||||
'filename' => $newFile];
|
||||
}
|
||||
|
||||
if ($this->quota) {
|
||||
$this->addQuotaEntry((int) $size, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* move an existing message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param string|\Laminas\Mail\Storage\Folder $folder name or instance of targer folder
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
*/
|
||||
public function moveMessage($id, $folder)
|
||||
{
|
||||
if (! ($folder instanceof Folder)) {
|
||||
$folder = $this->getFolders($folder);
|
||||
}
|
||||
|
||||
if ($folder->getGlobalName() == $this->currentFolder
|
||||
|| ($this->currentFolder == 'INBOX' && $folder->getGlobalName() == '/')
|
||||
) {
|
||||
throw new StorageException\RuntimeException('target is current folder');
|
||||
}
|
||||
|
||||
$filedata = $this->getFileData($id);
|
||||
$oldFile = $filedata['filename'];
|
||||
$flags = $filedata['flags'];
|
||||
|
||||
// moved message can't be recent
|
||||
while (($key = array_search(Storage::FLAG_RECENT, $flags)) !== false) {
|
||||
unset($flags[$key]);
|
||||
}
|
||||
$info = $this->getInfoString($flags);
|
||||
|
||||
// reserving a new name
|
||||
$tempFile = $this->createTmpFile($folder->getGlobalName());
|
||||
fclose($tempFile['handle']);
|
||||
|
||||
// we're adding the size to the filename for maildir++
|
||||
$size = filesize($oldFile);
|
||||
if ($size !== false) {
|
||||
$info = ',S=' . $size . $info;
|
||||
}
|
||||
|
||||
$newFile = $tempFile['dirname'] . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . $tempFile['uniq'] . $info;
|
||||
|
||||
// we're throwing any exception after removing our temp file and saving it to this variable instead
|
||||
$exception = null;
|
||||
|
||||
if (! rename($oldFile, $newFile)) {
|
||||
$exception = new StorageException\RuntimeException('cannot move message file');
|
||||
}
|
||||
|
||||
ErrorHandler::start(E_WARNING);
|
||||
unlink($tempFile['filename']);
|
||||
ErrorHandler::stop();
|
||||
|
||||
if ($exception) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
unset($this->files[$id - 1]);
|
||||
// remove the gap
|
||||
$this->files = array_values($this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* set flags for message
|
||||
*
|
||||
* NOTE: this method can't set the recent flag.
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param array $flags new flags for message
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
*/
|
||||
public function setFlags($id, $flags)
|
||||
{
|
||||
$info = $this->getInfoString($flags);
|
||||
$filedata = $this->getFileData($id);
|
||||
|
||||
// NOTE: double dirname to make sure we always move to cur. if recent
|
||||
// flag has been set (message is in new) it will be moved to cur.
|
||||
$newFilename = dirname(dirname($filedata['filename']))
|
||||
. DIRECTORY_SEPARATOR
|
||||
. 'cur'
|
||||
. DIRECTORY_SEPARATOR
|
||||
. "$filedata[uniq]$info";
|
||||
|
||||
ErrorHandler::start();
|
||||
$test = rename($filedata['filename'], $newFilename);
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $test) {
|
||||
throw new StorageException\RuntimeException('cannot rename file', 0, $error);
|
||||
}
|
||||
|
||||
$filedata['flags'] = $flags;
|
||||
$filedata['filename'] = $newFilename;
|
||||
|
||||
$this->files[$id - 1] = $filedata;
|
||||
}
|
||||
|
||||
/**
|
||||
* stub for not supported message deletion
|
||||
*
|
||||
* @param $id
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
*/
|
||||
public function removeMessage($id)
|
||||
{
|
||||
$filename = $this->getFileData($id, 'filename');
|
||||
|
||||
if ($this->quota) {
|
||||
$size = filesize($filename);
|
||||
}
|
||||
|
||||
ErrorHandler::start();
|
||||
$test = unlink($filename);
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $test) {
|
||||
throw new StorageException\RuntimeException('cannot remove message', 0, $error);
|
||||
}
|
||||
unset($this->files[$id - 1]);
|
||||
// remove the gap
|
||||
$this->files = array_values($this->files);
|
||||
if ($this->quota) {
|
||||
$this->addQuotaEntry(0 - (int) $size, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* enable/disable quota and set a quota value if wanted or needed
|
||||
*
|
||||
* You can enable/disable quota with true/false. If you don't have
|
||||
* a MDA or want to enforce a quota value you can also set this value
|
||||
* here. Use array('size' => SIZE_QUOTA, 'count' => MAX_MESSAGE) do
|
||||
* define your quota. Order of these fields does matter!
|
||||
*
|
||||
* @param bool|array $value new quota value
|
||||
*/
|
||||
public function setQuota($value)
|
||||
{
|
||||
$this->quota = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get currently set quota
|
||||
*
|
||||
* @see \Laminas\Mail\Storage\Writable\Maildir::setQuota()
|
||||
* @param bool $fromStorage
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
* @return bool|array
|
||||
*/
|
||||
public function getQuota($fromStorage = false)
|
||||
{
|
||||
if ($fromStorage) {
|
||||
ErrorHandler::start(E_WARNING);
|
||||
$fh = fopen($this->rootdir . 'maildirsize', 'r');
|
||||
$error = ErrorHandler::stop();
|
||||
if (! $fh) {
|
||||
throw new StorageException\RuntimeException('cannot open maildirsize', 0, $error);
|
||||
}
|
||||
$definition = fgets($fh);
|
||||
fclose($fh);
|
||||
$definition = explode(',', trim($definition));
|
||||
$quota = [];
|
||||
foreach ($definition as $member) {
|
||||
$key = $member[strlen($member) - 1];
|
||||
if ($key == 'S' || $key == 'C') {
|
||||
$key = $key == 'C' ? 'count' : 'size';
|
||||
}
|
||||
$quota[$key] = substr($member, 0, -1);
|
||||
}
|
||||
return $quota;
|
||||
}
|
||||
|
||||
return $this->quota;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating maildirsize"
|
||||
* @throws \Laminas\Mail\Storage\Exception\RuntimeException
|
||||
* @return array
|
||||
*/
|
||||
protected function calculateMaildirsize()
|
||||
{
|
||||
$timestamps = [];
|
||||
$messages = 0;
|
||||
$totalSize = 0;
|
||||
|
||||
if (is_array($this->quota)) {
|
||||
$quota = $this->quota;
|
||||
} else {
|
||||
try {
|
||||
$quota = $this->getQuota(true);
|
||||
} catch (StorageException\ExceptionInterface $e) {
|
||||
throw new StorageException\RuntimeException('no quota definition found', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
$folders = new RecursiveIteratorIterator($this->getFolders(), RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($folders as $folder) {
|
||||
$subdir = $folder->getGlobalName();
|
||||
if ($subdir == 'INBOX') {
|
||||
$subdir = '';
|
||||
} else {
|
||||
$subdir = '.' . $subdir;
|
||||
}
|
||||
if ($subdir == 'Trash') {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (['cur', 'new'] as $subsubdir) {
|
||||
$dirname = $this->rootdir . $subdir . DIRECTORY_SEPARATOR . $subsubdir . DIRECTORY_SEPARATOR;
|
||||
if (! file_exists($dirname)) {
|
||||
continue;
|
||||
}
|
||||
// NOTE: we are using mtime instead of "the latest timestamp". The latest would be atime
|
||||
// and as we are accessing the directory it would make the whole calculation useless.
|
||||
$timestamps[$dirname] = filemtime($dirname);
|
||||
|
||||
$dh = opendir($dirname);
|
||||
// NOTE: Should have been checked in constructor. Not throwing an exception here, quotas will
|
||||
// therefore not be fully enforced, but next request will fail anyway, if problem persists.
|
||||
if (! $dh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (($entry = readdir()) !== false) {
|
||||
if ($entry[0] == '.' || ! is_file($dirname . $entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($entry, ',S=')) {
|
||||
strtok($entry, '=');
|
||||
$filesize = strtok(':');
|
||||
if (is_numeric($filesize)) {
|
||||
$totalSize += $filesize;
|
||||
++$messages;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$size = filesize($dirname . $entry);
|
||||
if ($size === false) {
|
||||
// ignore, as we assume file got removed
|
||||
continue;
|
||||
}
|
||||
$totalSize += $size;
|
||||
++$messages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tmp = $this->createTmpFile();
|
||||
$fh = $tmp['handle'];
|
||||
$definition = [];
|
||||
foreach ($quota as $type => $value) {
|
||||
if ($type == 'size' || $type == 'count') {
|
||||
$type = $type == 'count' ? 'C' : 'S';
|
||||
}
|
||||
$definition[] = $value . $type;
|
||||
}
|
||||
$definition = implode(',', $definition);
|
||||
fwrite($fh, "$definition\n");
|
||||
fwrite($fh, "$totalSize $messages\n");
|
||||
fclose($fh);
|
||||
rename($tmp['filename'], $this->rootdir . 'maildirsize');
|
||||
foreach ($timestamps as $dir => $timestamp) {
|
||||
if ($timestamp < filemtime($dir)) {
|
||||
unlink($this->rootdir . 'maildirsize');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ['size' => $totalSize,
|
||||
'count' => $messages,
|
||||
'quota' => $quota];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating the quota for a Maildir++"
|
||||
* @param bool $forceRecalc
|
||||
* @return array
|
||||
*/
|
||||
protected function calculateQuota($forceRecalc = false)
|
||||
{
|
||||
$fh = null;
|
||||
$totalSize = 0;
|
||||
$messages = 0;
|
||||
$maildirsize = '';
|
||||
if (! $forceRecalc
|
||||
&& file_exists($this->rootdir . 'maildirsize')
|
||||
&& filesize($this->rootdir . 'maildirsize') < 5120
|
||||
) {
|
||||
$fh = fopen($this->rootdir . 'maildirsize', 'r');
|
||||
}
|
||||
if ($fh) {
|
||||
$maildirsize = fread($fh, 5120);
|
||||
if (strlen($maildirsize) >= 5120) {
|
||||
fclose($fh);
|
||||
$fh = null;
|
||||
$maildirsize = '';
|
||||
}
|
||||
}
|
||||
if (! $fh) {
|
||||
$result = $this->calculateMaildirsize();
|
||||
$totalSize = $result['size'];
|
||||
$messages = $result['count'];
|
||||
$quota = $result['quota'];
|
||||
} else {
|
||||
$maildirsize = explode("\n", $maildirsize);
|
||||
if (is_array($this->quota)) {
|
||||
$quota = $this->quota;
|
||||
} else {
|
||||
$definition = explode(',', $maildirsize[0]);
|
||||
$quota = [];
|
||||
foreach ($definition as $member) {
|
||||
$key = $member[strlen($member) - 1];
|
||||
if ($key == 'S' || $key == 'C') {
|
||||
$key = $key == 'C' ? 'count' : 'size';
|
||||
}
|
||||
$quota[$key] = substr($member, 0, -1);
|
||||
}
|
||||
}
|
||||
unset($maildirsize[0]);
|
||||
foreach ($maildirsize as $line) {
|
||||
list($size, $count) = explode(' ', trim($line));
|
||||
$totalSize += $size;
|
||||
$messages += $count;
|
||||
}
|
||||
}
|
||||
|
||||
$overQuota = false;
|
||||
$overQuota = $overQuota || (isset($quota['size']) && $totalSize > $quota['size']);
|
||||
$overQuota = $overQuota || (isset($quota['count']) && $messages > $quota['count']);
|
||||
// NOTE: $maildirsize equals false if it wasn't set (AKA we recalculated) or it's only
|
||||
// one line, because $maildirsize[0] gets unsetted.
|
||||
// Also we're using local time to calculate the 15 minute offset. Touching a file just for known the
|
||||
// local time of the file storage isn't worth the hassle.
|
||||
if ($overQuota && ($maildirsize || filemtime($this->rootdir . 'maildirsize') > time() - 900)) {
|
||||
$result = $this->calculateMaildirsize();
|
||||
$totalSize = $result['size'];
|
||||
$messages = $result['count'];
|
||||
$quota = $result['quota'];
|
||||
$overQuota = false;
|
||||
$overQuota = $overQuota || (isset($quota['size']) && $totalSize > $quota['size']);
|
||||
$overQuota = $overQuota || (isset($quota['count']) && $messages > $quota['count']);
|
||||
}
|
||||
|
||||
if ($fh) {
|
||||
// TODO is there a safe way to keep the handle open for writing?
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
return ['size' => $totalSize,
|
||||
'count' => $messages,
|
||||
'quota' => $quota,
|
||||
'over_quota' => $overQuota];
|
||||
}
|
||||
|
||||
protected function addQuotaEntry($size, $count = 1)
|
||||
{
|
||||
if (! file_exists($this->rootdir . 'maildirsize')) {
|
||||
// TODO: should get file handler from calculateQuota
|
||||
}
|
||||
$size = (int) $size;
|
||||
$count = (int) $count;
|
||||
file_put_contents($this->rootdir . 'maildirsize', "$size $count\n", FILE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if storage is currently over quota
|
||||
*
|
||||
* @see calculateQuota()
|
||||
* @param bool $detailedResponse return known data of quota and current size and message count
|
||||
* @param bool $forceRecalc
|
||||
* @return bool|array over quota state or detailed response
|
||||
*/
|
||||
public function checkQuota($detailedResponse = false, $forceRecalc = false)
|
||||
{
|
||||
$result = $this->calculateQuota($forceRecalc);
|
||||
return $detailedResponse ? $result : $result['over_quota'];
|
||||
}
|
||||
}
|
||||
92
vendor/laminas/laminas-mail/src/Storage/Writable/WritableInterface.php
vendored
Normal file
92
vendor/laminas/laminas-mail/src/Storage/Writable/WritableInterface.php
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Storage\Writable;
|
||||
|
||||
use Laminas\Mail\Message;
|
||||
use Laminas\Mail\Storage;
|
||||
use Laminas\Mime;
|
||||
|
||||
interface WritableInterface
|
||||
{
|
||||
/**
|
||||
* create a new folder
|
||||
*
|
||||
* This method also creates parent folders if necessary. Some mail storages
|
||||
* may restrict, which folder may be used as parent or which chars may be
|
||||
* used in the folder name
|
||||
*
|
||||
* @param string $name global name of folder, local name if $parentFolder
|
||||
* is set.
|
||||
* @param string|Storage\Folder $parentFolder parent folder for new folder,
|
||||
* else root folder is parent.
|
||||
* @throws Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function createFolder($name, $parentFolder = null);
|
||||
|
||||
/**
|
||||
* remove a folder
|
||||
*
|
||||
* @param string|Storage\Folder $name name or instance of folder.
|
||||
* @throws Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function removeFolder($name);
|
||||
|
||||
/**
|
||||
* rename and/or move folder
|
||||
*
|
||||
* The new name has the same restrictions as in createFolder()
|
||||
*
|
||||
* @param string|Storage\Folder $oldName name or instance of folder.
|
||||
* @param string $newName new global name of folder.
|
||||
* @throws Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function renameFolder($oldName, $newName);
|
||||
|
||||
/**
|
||||
* append a new message to mail storage
|
||||
*
|
||||
* @param string|Message|Mime\Message $message message as string or
|
||||
* instance of message class.
|
||||
* @param null|string|Storage\Folder $folder folder for new message, else
|
||||
* current folder is taken.
|
||||
* @param null|array $flags set flags for new message, else a default set
|
||||
* is used.
|
||||
* @throws Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function appendMessage($message, $folder = null, $flags = null);
|
||||
|
||||
/**
|
||||
* copy an existing message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param string|Storage\Folder $folder name or instance of target folder
|
||||
* @throws Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function copyMessage($id, $folder);
|
||||
|
||||
/**
|
||||
* move an existing message
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param string|Storage\Folder $folder name or instance of target folder
|
||||
* @throws Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function moveMessage($id, $folder);
|
||||
|
||||
/**
|
||||
* set flags for message
|
||||
*
|
||||
* NOTE: this method can't set the recent flag.
|
||||
*
|
||||
* @param int $id number of message
|
||||
* @param array $flags new flags for message
|
||||
* @throws Storage\Exception\ExceptionInterface
|
||||
*/
|
||||
public function setFlags($id, $flags);
|
||||
}
|
||||
64
vendor/laminas/laminas-mail/src/Transport/Envelope.php
vendored
Normal file
64
vendor/laminas/laminas-mail/src/Transport/Envelope.php
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport;
|
||||
|
||||
use Laminas\Stdlib\AbstractOptions;
|
||||
|
||||
class Envelope extends AbstractOptions
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $from;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $to;
|
||||
|
||||
/**
|
||||
* Get MAIL FROM
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set MAIL FROM
|
||||
*
|
||||
* @param string $from
|
||||
*/
|
||||
public function setFrom($from)
|
||||
{
|
||||
$this->from = (string) $from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get RCPT TO
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTo()
|
||||
{
|
||||
return $this->to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set RCPT TO
|
||||
*
|
||||
* @param string $to
|
||||
*/
|
||||
public function setTo($to)
|
||||
{
|
||||
$this->to = $to;
|
||||
}
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Transport/Exception/DomainException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Transport/Exception/DomainException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail\Transport component.
|
||||
*/
|
||||
class DomainException extends Exception\DomainException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
15
vendor/laminas/laminas-mail/src/Transport/Exception/ExceptionInterface.php
vendored
Normal file
15
vendor/laminas/laminas-mail/src/Transport/Exception/ExceptionInterface.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport\Exception;
|
||||
|
||||
use Laminas\Mail\Exception\ExceptionInterface as MailException;
|
||||
|
||||
interface ExceptionInterface extends MailException
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Transport/Exception/InvalidArgumentException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Transport/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class InvalidArgumentException extends Exception\InvalidArgumentException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
18
vendor/laminas/laminas-mail/src/Transport/Exception/RuntimeException.php
vendored
Normal file
18
vendor/laminas/laminas-mail/src/Transport/Exception/RuntimeException.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport\Exception;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
|
||||
/**
|
||||
* Exception for Laminas\Mail component.
|
||||
*/
|
||||
class RuntimeException extends Exception\RuntimeException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
85
vendor/laminas/laminas-mail/src/Transport/Factory.php
vendored
Normal file
85
vendor/laminas/laminas-mail/src/Transport/Factory.php
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport;
|
||||
|
||||
use Laminas\Stdlib\ArrayUtils;
|
||||
use Traversable;
|
||||
|
||||
abstract class Factory
|
||||
{
|
||||
/**
|
||||
* @var array Known transport types
|
||||
*/
|
||||
protected static $classMap = [
|
||||
'file' => File::class,
|
||||
'inmemory' => InMemory::class,
|
||||
'memory' => InMemory::class,
|
||||
'null' => InMemory::class,
|
||||
'sendmail' => Sendmail::class,
|
||||
'smtp' => Smtp::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array $spec
|
||||
* @return TransportInterface
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @throws Exception\DomainException
|
||||
*/
|
||||
public static function create($spec = [])
|
||||
{
|
||||
if ($spec instanceof Traversable) {
|
||||
$spec = ArrayUtils::iteratorToArray($spec);
|
||||
}
|
||||
|
||||
if (! is_array($spec)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects an array or Traversable argument; received "%s"',
|
||||
__METHOD__,
|
||||
(is_object($spec) ? get_class($spec) : gettype($spec))
|
||||
));
|
||||
}
|
||||
|
||||
$type = isset($spec['type']) ? $spec['type'] : 'sendmail';
|
||||
|
||||
$normalizedType = strtolower($type);
|
||||
|
||||
if (isset(static::$classMap[$normalizedType])) {
|
||||
$type = static::$classMap[$normalizedType];
|
||||
}
|
||||
|
||||
if (! class_exists($type)) {
|
||||
throw new Exception\DomainException(sprintf(
|
||||
'%s expects the "type" attribute to resolve to an existing class; received "%s"',
|
||||
__METHOD__,
|
||||
$type
|
||||
));
|
||||
}
|
||||
|
||||
$transport = new $type;
|
||||
|
||||
if (! $transport instanceof TransportInterface) {
|
||||
throw new Exception\DomainException(sprintf(
|
||||
'%s expects the "type" attribute to resolve to a valid %s instance; received "%s"',
|
||||
__METHOD__,
|
||||
TransportInterface::class,
|
||||
$type
|
||||
));
|
||||
}
|
||||
|
||||
if ($transport instanceof Smtp && isset($spec['options'])) {
|
||||
$transport->setOptions(new SmtpOptions($spec['options']));
|
||||
}
|
||||
|
||||
if ($transport instanceof File && isset($spec['options'])) {
|
||||
$transport->setOptions(new FileOptions($spec['options']));
|
||||
}
|
||||
|
||||
return $transport;
|
||||
}
|
||||
}
|
||||
96
vendor/laminas/laminas-mail/src/Transport/File.php
vendored
Normal file
96
vendor/laminas/laminas-mail/src/Transport/File.php
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport;
|
||||
|
||||
use Laminas\Mail\Message;
|
||||
|
||||
/**
|
||||
* File transport
|
||||
*
|
||||
* Class for saving outgoing emails in filesystem
|
||||
*/
|
||||
class File implements TransportInterface
|
||||
{
|
||||
/**
|
||||
* @var FileOptions
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Last file written to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $lastFile;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param null|FileOptions $options OPTIONAL (Default: null)
|
||||
*/
|
||||
public function __construct(FileOptions $options = null)
|
||||
{
|
||||
if (! $options instanceof FileOptions) {
|
||||
$options = new FileOptions();
|
||||
}
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FileOptions
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets options
|
||||
*
|
||||
* @param FileOptions $options
|
||||
*/
|
||||
public function setOptions(FileOptions $options)
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves e-mail message to a file
|
||||
*
|
||||
* @param Message $message
|
||||
* @throws Exception\RuntimeException on not writable target directory or
|
||||
* on file_put_contents() failure
|
||||
*/
|
||||
public function send(Message $message)
|
||||
{
|
||||
$options = $this->options;
|
||||
$filename = call_user_func($options->getCallback(), $this);
|
||||
$file = $options->getPath() . DIRECTORY_SEPARATOR . $filename;
|
||||
$email = $message->toString();
|
||||
|
||||
if (false === file_put_contents($file, $email)) {
|
||||
throw new Exception\RuntimeException(sprintf(
|
||||
'Unable to write mail to file (directory "%s")',
|
||||
$options->getPath()
|
||||
));
|
||||
}
|
||||
|
||||
$this->lastFile = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the last file written to
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastFile()
|
||||
{
|
||||
return $this->lastFile;
|
||||
}
|
||||
}
|
||||
95
vendor/laminas/laminas-mail/src/Transport/FileOptions.php
vendored
Normal file
95
vendor/laminas/laminas-mail/src/Transport/FileOptions.php
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport;
|
||||
|
||||
use Laminas\Mail\Exception;
|
||||
use Laminas\Stdlib\AbstractOptions;
|
||||
|
||||
class FileOptions extends AbstractOptions
|
||||
{
|
||||
/**
|
||||
* @var string Path to stored mail files
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* Set path to stored mail files
|
||||
*
|
||||
* @param string $path
|
||||
* @throws \Laminas\Mail\Exception\InvalidArgumentException
|
||||
* @return FileOptions
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
if (! is_dir($path) || ! is_writable($path)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a valid path in which to write mail files; received "%s"',
|
||||
__METHOD__,
|
||||
(string) $path
|
||||
));
|
||||
}
|
||||
$this->path = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path
|
||||
*
|
||||
* If none is set, uses value from sys_get_temp_dir()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
if (null === $this->path) {
|
||||
$this->setPath(sys_get_temp_dir());
|
||||
}
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set callback used to generate a file name
|
||||
*
|
||||
* @param callable $callback
|
||||
* @throws \Laminas\Mail\Exception\InvalidArgumentException
|
||||
* @return FileOptions
|
||||
*/
|
||||
public function setCallback($callback)
|
||||
{
|
||||
if (! is_callable($callback)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a valid callback; received "%s"',
|
||||
__METHOD__,
|
||||
(is_object($callback) ? get_class($callback) : gettype($callback))
|
||||
));
|
||||
}
|
||||
$this->callback = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get callback used to generate a file name
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public function getCallback()
|
||||
{
|
||||
if (null === $this->callback) {
|
||||
$this->setCallback(function () {
|
||||
return 'LaminasMail_' . time() . '_' . mt_rand() . '.eml';
|
||||
});
|
||||
}
|
||||
return $this->callback;
|
||||
}
|
||||
}
|
||||
46
vendor/laminas/laminas-mail/src/Transport/InMemory.php
vendored
Normal file
46
vendor/laminas/laminas-mail/src/Transport/InMemory.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport;
|
||||
|
||||
use Laminas\Mail\Message;
|
||||
|
||||
/**
|
||||
* InMemory transport
|
||||
*
|
||||
* This transport will just store the message in memory. It is helpful
|
||||
* when unit testing, or to prevent sending email when in development or
|
||||
* testing.
|
||||
*/
|
||||
class InMemory implements TransportInterface
|
||||
{
|
||||
/**
|
||||
* @var null|Message
|
||||
*/
|
||||
protected $lastMessage;
|
||||
|
||||
/**
|
||||
* Takes the last message and saves it for testing.
|
||||
*
|
||||
* @param Message $message
|
||||
*/
|
||||
public function send(Message $message)
|
||||
{
|
||||
$this->lastMessage = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last message sent.
|
||||
*
|
||||
* @return null|Message
|
||||
*/
|
||||
public function getLastMessage()
|
||||
{
|
||||
return $this->lastMessage;
|
||||
}
|
||||
}
|
||||
338
vendor/laminas/laminas-mail/src/Transport/Sendmail.php
vendored
Normal file
338
vendor/laminas/laminas-mail/src/Transport/Sendmail.php
vendored
Normal file
@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport;
|
||||
|
||||
use Laminas\Mail;
|
||||
use Laminas\Mail\Address\AddressInterface;
|
||||
use Laminas\Mail\Header\HeaderInterface;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Class for sending email via the PHP internal mail() function
|
||||
*/
|
||||
class Sendmail implements TransportInterface
|
||||
{
|
||||
/**
|
||||
* Config options for sendmail parameters
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* Callback to use when sending mail; typically, {@link mailHandler()}
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $callable;
|
||||
|
||||
/**
|
||||
* error information
|
||||
* @var string
|
||||
*/
|
||||
protected $errstr;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $operatingSystem;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param null|string|array|Traversable $parameters OPTIONAL (Default: null)
|
||||
*/
|
||||
public function __construct($parameters = null)
|
||||
{
|
||||
if ($parameters !== null) {
|
||||
$this->setParameters($parameters);
|
||||
}
|
||||
$this->callable = [$this, 'mailHandler'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sendmail parameters
|
||||
*
|
||||
* Used to populate the additional_parameters argument to mail()
|
||||
*
|
||||
* @param null|string|array|Traversable $parameters
|
||||
* @throws \Laminas\Mail\Transport\Exception\InvalidArgumentException
|
||||
* @return Sendmail
|
||||
*/
|
||||
public function setParameters($parameters)
|
||||
{
|
||||
if ($parameters === null || is_string($parameters)) {
|
||||
$this->parameters = $parameters;
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (! is_array($parameters) && ! $parameters instanceof Traversable) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a string, array, or Traversable object of parameters; received "%s"',
|
||||
__METHOD__,
|
||||
(is_object($parameters) ? get_class($parameters) : gettype($parameters))
|
||||
));
|
||||
}
|
||||
|
||||
$string = '';
|
||||
foreach ($parameters as $param) {
|
||||
$string .= ' ' . $param;
|
||||
}
|
||||
|
||||
$this->parameters = trim($string);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set callback to use for mail
|
||||
*
|
||||
* Primarily for testing purposes, but could be used to curry arguments.
|
||||
*
|
||||
* @param callable $callable
|
||||
* @throws \Laminas\Mail\Transport\Exception\InvalidArgumentException
|
||||
* @return Sendmail
|
||||
*/
|
||||
public function setCallable($callable)
|
||||
{
|
||||
if (! is_callable($callable)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'%s expects a callable argument; received "%s"',
|
||||
__METHOD__,
|
||||
(is_object($callable) ? get_class($callable) : gettype($callable))
|
||||
));
|
||||
}
|
||||
$this->callable = $callable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message
|
||||
*
|
||||
* @param \Laminas\Mail\Message $message
|
||||
*/
|
||||
public function send(Mail\Message $message)
|
||||
{
|
||||
$to = $this->prepareRecipients($message);
|
||||
$subject = $this->prepareSubject($message);
|
||||
$body = $this->prepareBody($message);
|
||||
$headers = $this->prepareHeaders($message);
|
||||
$params = $this->prepareParameters($message);
|
||||
|
||||
// On *nix platforms, we need to replace \r\n with \n
|
||||
// sendmail is not an SMTP server, it is a unix command - it expects LF
|
||||
if (! $this->isWindowsOs()) {
|
||||
$to = str_replace("\r\n", "\n", $to);
|
||||
$subject = str_replace("\r\n", "\n", $subject);
|
||||
$body = str_replace("\r\n", "\n", $body);
|
||||
$headers = str_replace("\r\n", "\n", $headers);
|
||||
}
|
||||
|
||||
call_user_func($this->callable, $to, $subject, $body, $headers, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare recipients list
|
||||
*
|
||||
* @param \Laminas\Mail\Message $message
|
||||
* @throws \Laminas\Mail\Transport\Exception\RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareRecipients(Mail\Message $message)
|
||||
{
|
||||
$headers = $message->getHeaders();
|
||||
|
||||
$hasTo = $headers->has('to');
|
||||
if (! $hasTo && ! $headers->has('cc') && ! $headers->has('bcc')) {
|
||||
throw new Exception\RuntimeException(
|
||||
'Invalid email; contains no at least one of "To", "Cc", and "Bcc" header'
|
||||
);
|
||||
}
|
||||
|
||||
if (! $hasTo) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @var Mail\Header\To $to */
|
||||
$to = $headers->get('to');
|
||||
$list = $to->getAddressList();
|
||||
if (0 == count($list)) {
|
||||
throw new Exception\RuntimeException('Invalid "To" header; contains no addresses');
|
||||
}
|
||||
|
||||
// If not on Windows, return normal string
|
||||
if (! $this->isWindowsOs()) {
|
||||
return $to->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
}
|
||||
|
||||
// Otherwise, return list of emails
|
||||
$addresses = [];
|
||||
foreach ($list as $address) {
|
||||
$addresses[] = $address->getEmail();
|
||||
}
|
||||
$addresses = implode(', ', $addresses);
|
||||
return $addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the subject line string
|
||||
*
|
||||
* @param \Laminas\Mail\Message $message
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareSubject(Mail\Message $message)
|
||||
{
|
||||
$headers = $message->getHeaders();
|
||||
if (! $headers->has('subject')) {
|
||||
return;
|
||||
}
|
||||
$header = $headers->get('subject');
|
||||
return $header->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the body string
|
||||
*
|
||||
* @param \Laminas\Mail\Message $message
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareBody(Mail\Message $message)
|
||||
{
|
||||
if (! $this->isWindowsOs()) {
|
||||
// *nix platforms can simply return the body text
|
||||
return $message->getBodyText();
|
||||
}
|
||||
|
||||
// On windows, lines beginning with a full stop need to be fixed
|
||||
$text = $message->getBodyText();
|
||||
$text = str_replace("\n.", "\n..", $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the textual representation of headers
|
||||
*
|
||||
* @param \Laminas\Mail\Message $message
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareHeaders(Mail\Message $message)
|
||||
{
|
||||
// On Windows, simply return verbatim
|
||||
if ($this->isWindowsOs()) {
|
||||
return $message->getHeaders()->toString();
|
||||
}
|
||||
|
||||
// On *nix platforms, strip the "to" header
|
||||
$headers = clone $message->getHeaders();
|
||||
$headers->removeHeader('To');
|
||||
$headers->removeHeader('Subject');
|
||||
|
||||
/** @var Mail\Header\From $from Sanitize the From header*/
|
||||
$from = $headers->get('From');
|
||||
if ($from) {
|
||||
foreach ($from->getAddressList() as $address) {
|
||||
if (strpos($address->getEmail(), '\\"') !== false) {
|
||||
throw new Exception\RuntimeException('Potential code injection in From header');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $headers->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare additional_parameters argument
|
||||
*
|
||||
* Basically, overrides the MAIL FROM envelope with either the Sender or
|
||||
* From address.
|
||||
*
|
||||
* @param \Laminas\Mail\Message $message
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareParameters(Mail\Message $message)
|
||||
{
|
||||
if ($this->isWindowsOs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameters = (string) $this->parameters;
|
||||
|
||||
$sender = $message->getSender();
|
||||
if ($sender instanceof AddressInterface) {
|
||||
$parameters .= ' -f' . \escapeshellarg($sender->getEmail());
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
$from = $message->getFrom();
|
||||
if (count($from)) {
|
||||
$from->rewind();
|
||||
$sender = $from->current();
|
||||
$parameters .= ' -f' . \escapeshellarg($sender->getEmail());
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail using PHP native mail()
|
||||
*
|
||||
* @param string $to
|
||||
* @param string $subject
|
||||
* @param string $message
|
||||
* @param string $headers
|
||||
* @param $parameters
|
||||
* @throws \Laminas\Mail\Transport\Exception\RuntimeException
|
||||
*/
|
||||
public function mailHandler($to, $subject, $message, $headers, $parameters)
|
||||
{
|
||||
set_error_handler([$this, 'handleMailErrors']);
|
||||
if ($parameters === null) {
|
||||
$result = mail($to, $subject, $message, $headers);
|
||||
} else {
|
||||
$result = mail($to, $subject, $message, $headers, $parameters);
|
||||
}
|
||||
restore_error_handler();
|
||||
|
||||
if ($this->errstr !== null || ! $result) {
|
||||
$errstr = $this->errstr;
|
||||
if (empty($errstr)) {
|
||||
$errstr = 'Unknown error';
|
||||
}
|
||||
throw new Exception\RuntimeException('Unable to send mail: ' . $errstr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary error handler for PHP native mail().
|
||||
*
|
||||
* @param int $errno
|
||||
* @param string $errstr
|
||||
* @param string $errfile
|
||||
* @param string $errline
|
||||
* @param array $errcontext
|
||||
* @return bool always true
|
||||
*/
|
||||
public function handleMailErrors($errno, $errstr, $errfile = null, $errline = null, array $errcontext = null)
|
||||
{
|
||||
$this->errstr = $errstr;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a windows OS?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isWindowsOs()
|
||||
{
|
||||
if (! $this->operatingSystem) {
|
||||
$this->operatingSystem = strtoupper(substr(PHP_OS, 0, 3));
|
||||
}
|
||||
return ($this->operatingSystem == 'WIN');
|
||||
}
|
||||
}
|
||||
406
vendor/laminas/laminas-mail/src/Transport/Smtp.php
vendored
Normal file
406
vendor/laminas/laminas-mail/src/Transport/Smtp.php
vendored
Normal file
@ -0,0 +1,406 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
||||
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
||||
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
||||
*/
|
||||
|
||||
namespace Laminas\Mail\Transport;
|
||||
|
||||
use Laminas\Mail\Address;
|
||||
use Laminas\Mail\Headers;
|
||||
use Laminas\Mail\Message;
|
||||
use Laminas\Mail\Protocol;
|
||||
use Laminas\Mail\Protocol\Exception as ProtocolException;
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
|
||||
/**
|
||||
* SMTP connection object
|
||||
*
|
||||
* Loads an instance of Laminas\Mail\Protocol\Smtp and forwards smtp transactions
|
||||
*/
|
||||
class Smtp implements TransportInterface
|
||||
{
|
||||
/**
|
||||
* @var SmtpOptions
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var Envelope|null
|
||||
*/
|
||||
protected $envelope;
|
||||
|
||||
/**
|
||||
* @var Protocol\Smtp
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoDisconnect = true;
|
||||
|
||||
/**
|
||||
* @var Protocol\SmtpPluginManager
|
||||
*/
|
||||
protected $plugins;
|
||||
|
||||
/**
|
||||
* When did we connect to the server?
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $connectedTime;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param SmtpOptions $options Optional
|
||||
*/
|
||||
public function __construct(SmtpOptions $options = null)
|
||||
{
|
||||
if (! $options instanceof SmtpOptions) {
|
||||
$options = new SmtpOptions();
|
||||
}
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options
|
||||
*
|
||||
* @param SmtpOptions $options
|
||||
* @return Smtp
|
||||
*/
|
||||
public function setOptions(SmtpOptions $options)
|
||||
{
|
||||
$this->options = $options;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options
|
||||
*
|
||||
* @return SmtpOptions
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options
|
||||
*
|
||||
* @param Envelope $envelope
|
||||
*/
|
||||
public function setEnvelope(Envelope $envelope)
|
||||
{
|
||||
$this->envelope = $envelope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get envelope
|
||||
*
|
||||
* @return Envelope|null
|
||||
*/
|
||||
public function getEnvelope()
|
||||
{
|
||||
return $this->envelope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plugin manager for obtaining SMTP protocol connection
|
||||
*
|
||||
* @param Protocol\SmtpPluginManager $plugins
|
||||
* @throws Exception\InvalidArgumentException
|
||||
* @return Smtp
|
||||
*/
|
||||
public function setPluginManager(Protocol\SmtpPluginManager $plugins)
|
||||
{
|
||||
$this->plugins = $plugins;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin manager for loading SMTP protocol connection
|
||||
*
|
||||
* @return Protocol\SmtpPluginManager
|
||||
*/
|
||||
public function getPluginManager()
|
||||
{
|
||||
if (null === $this->plugins) {
|
||||
$this->setPluginManager(new Protocol\SmtpPluginManager(new ServiceManager()));
|
||||
}
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the automatic disconnection when destruct
|
||||
*
|
||||
* @param bool $flag
|
||||
* @return Smtp
|
||||
*/
|
||||
public function setAutoDisconnect($flag)
|
||||
{
|
||||
$this->autoDisconnect = (bool) $flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the automatic disconnection value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAutoDisconnect()
|
||||
{
|
||||
return $this->autoDisconnect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an SMTP connection
|
||||
*
|
||||
* @param string $name
|
||||
* @param array|null $options
|
||||
* @return Protocol\Smtp
|
||||
*/
|
||||
public function plugin($name, array $options = null)
|
||||
{
|
||||
return $this->getPluginManager()->get($name, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class destructor to ensure all open connections are closed
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (! $this->getConnection() instanceof Protocol\Smtp) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->getConnection()->quit();
|
||||
} catch (ProtocolException\ExceptionInterface $e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if ($this->autoDisconnect) {
|
||||
$this->getConnection()->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection protocol instance
|
||||
*
|
||||
* @param Protocol\AbstractProtocol $connection
|
||||
*/
|
||||
public function setConnection(Protocol\AbstractProtocol $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
if (($connection instanceof Protocol\Smtp)
|
||||
&& ($this->getOptions()->getConnectionTimeLimit() !== null)
|
||||
) {
|
||||
$connection->setUseCompleteQuit(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the connection protocol instance
|
||||
*
|
||||
* @return Protocol\Smtp
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
$timeLimit = $this->getOptions()->getConnectionTimeLimit();
|
||||
if ($timeLimit !== null
|
||||
&& $this->connectedTime !== null
|
||||
&& ((time() - $this->connectedTime) > $timeLimit)
|
||||
) {
|
||||
$this->connection = null;
|
||||
}
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect the connection protocol instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
if ($this->getConnection() instanceof Protocol\Smtp) {
|
||||
$this->getConnection()->disconnect();
|
||||
$this->connectedTime = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email via the SMTP connection protocol
|
||||
*
|
||||
* The connection via the protocol adapter is made just-in-time to allow a
|
||||
* developer to add a custom adapter if required before mail is sent.
|
||||
*
|
||||
* @param Message $message
|
||||
* @throws Exception\RuntimeException
|
||||
*/
|
||||
public function send(Message $message)
|
||||
{
|
||||
// If sending multiple messages per session use existing adapter
|
||||
$connection = $this->getConnection();
|
||||
|
||||
if (! ($connection instanceof Protocol\Smtp) || ! $connection->hasSession()) {
|
||||
$connection = $this->connect();
|
||||
} else {
|
||||
// Reset connection to ensure reliable transaction
|
||||
$connection->rset();
|
||||
}
|
||||
|
||||
// Prepare message
|
||||
$from = $this->prepareFromAddress($message);
|
||||
$recipients = $this->prepareRecipients($message);
|
||||
$headers = $this->prepareHeaders($message);
|
||||
$body = $this->prepareBody($message);
|
||||
|
||||
if ((count($recipients) == 0) && (! empty($headers) || ! empty($body))) {
|
||||
// Per RFC 2821 3.3 (page 18)
|
||||
throw new Exception\RuntimeException(
|
||||
sprintf(
|
||||
'%s transport expects at least one recipient if the message has at least one header or body',
|
||||
__CLASS__
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Set sender email address
|
||||
$connection->mail($from);
|
||||
|
||||
// Set recipient forward paths
|
||||
foreach ($recipients as $recipient) {
|
||||
$connection->rcpt($recipient);
|
||||
}
|
||||
|
||||
// Issue DATA command to client
|
||||
$connection->data($headers . Headers::EOL . $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve email address for envelope FROM
|
||||
*
|
||||
* @param Message $message
|
||||
* @throws Exception\RuntimeException
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareFromAddress(Message $message)
|
||||
{
|
||||
if ($this->getEnvelope() && $this->getEnvelope()->getFrom()) {
|
||||
return $this->getEnvelope()->getFrom();
|
||||
}
|
||||
|
||||
$sender = $message->getSender();
|
||||
if ($sender instanceof Address\AddressInterface) {
|
||||
return $sender->getEmail();
|
||||
}
|
||||
|
||||
$from = $message->getFrom();
|
||||
if (! count($from)) {
|
||||
// Per RFC 2822 3.6
|
||||
throw new Exception\RuntimeException(sprintf(
|
||||
'%s transport expects either a Sender or at least one From address in the Message; none provided',
|
||||
__CLASS__
|
||||
));
|
||||
}
|
||||
|
||||
$from->rewind();
|
||||
$sender = $from->current();
|
||||
return $sender->getEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare array of email address recipients
|
||||
*
|
||||
* @param Message $message
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareRecipients(Message $message)
|
||||
{
|
||||
if ($this->getEnvelope() && $this->getEnvelope()->getTo()) {
|
||||
return (array) $this->getEnvelope()->getTo();
|
||||
}
|
||||
|
||||
$recipients = [];
|
||||
foreach ($message->getTo() as $address) {
|
||||
$recipients[] = $address->getEmail();
|
||||
}
|
||||
foreach ($message->getCc() as $address) {
|
||||
$recipients[] = $address->getEmail();
|
||||
}
|
||||
foreach ($message->getBcc() as $address) {
|
||||
$recipients[] = $address->getEmail();
|
||||
}
|
||||
|
||||
$recipients = array_unique($recipients);
|
||||
return $recipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare header string from message
|
||||
*
|
||||
* @param Message $message
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareHeaders(Message $message)
|
||||
{
|
||||
$headers = clone $message->getHeaders();
|
||||
$headers->removeHeader('Bcc');
|
||||
return $headers->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare body string from message
|
||||
*
|
||||
* @param Message $message
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareBody(Message $message)
|
||||
{
|
||||
return $message->getBodyText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy load the connection
|
||||
*
|
||||
* @return Protocol\Smtp
|
||||
*/
|
||||
protected function lazyLoadConnection()
|
||||
{
|
||||
// Check if authentication is required and determine required class
|
||||
$options = $this->getOptions();
|
||||
$config = $options->getConnectionConfig();
|
||||
$config['host'] = $options->getHost();
|
||||
$config['port'] = $options->getPort();
|
||||
|
||||
$this->setConnection($this->plugin($options->getConnectionClass(), $config));
|
||||
|
||||
return $this->connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect the connection, and pass it helo
|
||||
*
|
||||
* @return Protocol\Smtp
|
||||
*/
|
||||
protected function connect()
|
||||
{
|
||||
if (! $this->connection instanceof Protocol\Smtp) {
|
||||
return $this->lazyLoadConnection();
|
||||
}
|
||||
|
||||
$this->connection->connect();
|
||||
|
||||
$this->connectedTime = time();
|
||||
|
||||
$this->connection->helo($this->getOptions()->getName());
|
||||
|
||||
return $this->connection;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user