commit vendor

This commit is contained in:
2025-11-11 14:49:30 +01:00
parent f33121a308
commit 6d03080c00
2436 changed files with 483781 additions and 0 deletions

View File

@ -0,0 +1,2 @@
Copyright (c) 2019-2020, Laminas Foundation.
All rights reserved. (https://getlaminas.org/)

27
vendor/laminas/laminas-mime/LICENSE.md vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2019-2020, Laminas Foundation
All rights reserved.
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.

View File

@ -0,0 +1,228 @@
<?php
/**
* @see https://github.com/laminas/laminas-mime for the canonical source repository
* @copyright https://github.com/laminas/laminas-mime/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-mime/blob/master/LICENSE.md New BSD License
*/
namespace Laminas\Mime;
use Laminas\Mail\Headers;
use Laminas\Stdlib\ErrorHandler;
class Decode
{
/**
* Explode MIME multipart string into separate parts
*
* Parts consist of the header and the body of each MIME part.
*
* @param string $body raw body of message
* @param string $boundary boundary as found in content-type
* @return array parts with content of each part, empty if no parts found
* @throws Exception\RuntimeException
*/
public static function splitMime($body, $boundary)
{
// TODO: we're ignoring \r for now - is this function fast enough and is it safe to assume noone needs \r?
$body = str_replace("\r", '', $body);
$start = 0;
$res = [];
// find every mime part limiter and cut out the
// string before it.
// the part before the first boundary string is discarded:
$p = strpos($body, '--' . $boundary . "\n", $start);
if ($p === false) {
// no parts found!
return [];
}
// position after first boundary line
$start = $p + 3 + strlen($boundary);
while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) {
$res[] = substr($body, $start, $p - $start);
$start = $p + 3 + strlen($boundary);
}
// no more parts, find end boundary
$p = strpos($body, '--' . $boundary . '--', $start);
if ($p === false) {
throw new Exception\RuntimeException('Not a valid Mime Message: End Missing');
}
// the remaining part also needs to be parsed:
$res[] = substr($body, $start, $p - $start);
return $res;
}
/**
* decodes a mime encoded String and returns a
* struct of parts with header and body
*
* @param string $message raw message content
* @param string $boundary boundary as found in content-type
* @param string $EOL EOL string; defaults to {@link Laminas\Mime\Mime::LINEEND}
* @return array|null parts as array('header' => array(name => value), 'body' => content), null if no parts found
* @throws Exception\RuntimeException
*/
public static function splitMessageStruct($message, $boundary, $EOL = Mime::LINEEND)
{
$parts = static::splitMime($message, $boundary);
if (! $parts) {
return;
}
$result = [];
$headers = null; // "Declare" variable before the first usage "for reading"
$body = null; // "Declare" variable before the first usage "for reading"
foreach ($parts as $part) {
static::splitMessage($part, $headers, $body, $EOL);
$result[] = [
'header' => $headers,
'body' => $body,
];
}
return $result;
}
/**
* split a message in header and body part, if no header or an
* invalid header is found $headers is empty
*
* The charset of the returned headers depend on your iconv settings.
*
* @param string|Headers $message raw message with header and optional content
* @param Headers $headers output param, headers container
* @param string $body output param, content of message
* @param string $EOL EOL string; defaults to {@link Laminas\Mime\Mime::LINEEND}
* @param bool $strict enable strict mode for parsing message
* @return null
*/
public static function splitMessage($message, &$headers, &$body, $EOL = Mime::LINEEND, $strict = false)
{
if ($message instanceof Headers) {
$message = $message->toString();
}
// check for valid header at first line
$firstlinePos = strpos($message, "\n");
$firstline = $firstlinePos === false ? $message : substr($message, 0, $firstlinePos);
if (! preg_match('%^[^\s]+[^:]*:%', $firstline)) {
$headers = new Headers();
// TODO: we're ignoring \r for now - is this function fast enough and is it safe to assume noone needs \r?
$body = str_replace(["\r", "\n"], ['', $EOL], $message);
return;
}
// see @Laminas-372, pops the first line off a message if it doesn't contain a header
if (! $strict) {
$parts = explode(':', $firstline, 2);
if (count($parts) != 2) {
$message = substr($message, strpos($message, $EOL) + 1);
}
}
// @todo splitMime removes "\r" sequences, which breaks valid mime
// messages as returned by many mail servers
$headersEOL = $EOL;
// find an empty line between headers and body
// default is set new line
// @todo Maybe this is too much "magic"; we should be more strict here
if (strpos($message, $EOL . $EOL)) {
list($headers, $body) = explode($EOL . $EOL, $message, 2);
// next is the standard new line
} elseif ($EOL != "\r\n" && strpos($message, "\r\n\r\n")) {
list($headers, $body) = explode("\r\n\r\n", $message, 2);
$headersEOL = "\r\n"; // Headers::fromString will fail with incorrect EOL
// next is the other "standard" new line
} elseif ($EOL != "\n" && strpos($message, "\n\n")) {
list($headers, $body) = explode("\n\n", $message, 2);
$headersEOL = "\n";
// at last resort find anything that looks like a new line
} else {
ErrorHandler::start(E_NOTICE | E_WARNING);
list($headers, $body) = preg_split("%([\r\n]+)\\1%U", $message, 2);
ErrorHandler::stop();
}
$headers = Headers::fromString($headers, $headersEOL);
}
/**
* split a content type in its different parts
*
* @param string $type content-type
* @param string $wantedPart the wanted part, else an array with all parts is returned
* @return string|array wanted part or all parts as array('type' => content-type, partname => value)
*/
public static function splitContentType($type, $wantedPart = null)
{
return static::splitHeaderField($type, $wantedPart, 'type');
}
/**
* split a header field like content type in its different parts
*
* @param string $field header field
* @param string $wantedPart the wanted part, else 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 Exception\RuntimeException
*/
public static function splitHeaderField($field, $wantedPart = null, $firstName = '0')
{
$wantedPart = strtolower($wantedPart);
$firstName = strtolower($firstName);
// special case - a bit optimized
if ($firstName === $wantedPart) {
$field = strtok($field, ';');
return $field[0] == '"' ? substr($field, 1, -1) : $field;
}
$field = $firstName . '=' . $field;
if (! preg_match_all('%([^=\s]+)\s*=\s*("[^"]+"|[^;]+)(;\s*|$)%', $field, $matches)) {
throw new Exception\RuntimeException('not a valid header field');
}
if ($wantedPart) {
foreach ($matches[1] as $key => $name) {
if (strcasecmp($name, $wantedPart)) {
continue;
}
if ($matches[2][$key][0] != '"') {
return $matches[2][$key];
}
return substr($matches[2][$key], 1, -1);
}
return;
}
$split = [];
foreach ($matches[1] as $key => $name) {
$name = strtolower($name);
if ($matches[2][$key][0] == '"') {
$split[$name] = substr($matches[2][$key], 1, -1);
} else {
$split[$name] = $matches[2][$key];
}
}
return $split;
}
/**
* decode a quoted printable encoded string
*
* The charset of the returned string depends on your iconv settings.
*
* @param string $string encoded string
* @return string decoded string
*/
public static function decodeQuotedPrintable($string)
{
return iconv_mime_decode($string, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
}
}

View File

@ -0,0 +1,13 @@
<?php
/**
* @see https://github.com/laminas/laminas-mime for the canonical source repository
* @copyright https://github.com/laminas/laminas-mime/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-mime/blob/master/LICENSE.md New BSD License
*/
namespace Laminas\Mime\Exception;
interface ExceptionInterface
{
}

View File

@ -0,0 +1,14 @@
<?php
/**
* @see https://github.com/laminas/laminas-mime for the canonical source repository
* @copyright https://github.com/laminas/laminas-mime/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-mime/blob/master/LICENSE.md New BSD License
*/
namespace Laminas\Mime\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements
ExceptionInterface
{
}

View File

@ -0,0 +1,16 @@
<?php
/**
* @see https://github.com/laminas/laminas-mime for the canonical source repository
* @copyright https://github.com/laminas/laminas-mime/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-mime/blob/master/LICENSE.md New BSD License
*/
namespace Laminas\Mime\Exception;
/**
* Exception for Laminas\Mime component.
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@ -0,0 +1,310 @@
<?php
/**
* @see https://github.com/laminas/laminas-mime for the canonical source repository
* @copyright https://github.com/laminas/laminas-mime/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-mime/blob/master/LICENSE.md New BSD License
*/
namespace Laminas\Mime;
class Message
{
protected $parts = [];
protected $mime = null;
/**
* Returns the list of all Laminas\Mime\Part in the message
*
* @return Part[]
*/
public function getParts()
{
return $this->parts;
}
/**
* Sets the given array of Laminas\Mime\Part as the array for the message
*
* @param array $parts
* @return self
*/
public function setParts($parts)
{
$this->parts = $parts;
return $this;
}
/**
* Append a new Laminas\Mime\Part to the current message
*
* @param \Laminas\Mime\Part $part
* @throws Exception\InvalidArgumentException
* @return self
*/
public function addPart(Part $part)
{
foreach ($this->getParts() as $key => $row) {
if ($part == $row) {
throw new Exception\InvalidArgumentException(sprintf(
'Provided part %s already defined.',
$part->getId()
));
}
}
$this->parts[] = $part;
return $this;
}
/**
* Check if message needs to be sent as multipart
* MIME message or if it has only one part.
*
* @return bool
*/
public function isMultiPart()
{
return (count($this->parts) > 1);
}
/**
* Set Laminas\Mime\Mime object for the message
*
* This can be used to set the boundary specifically or to use a subclass of
* Laminas\Mime for generating the boundary.
*
* @param \Laminas\Mime\Mime $mime
* @return self
*/
public function setMime(Mime $mime)
{
$this->mime = $mime;
return $this;
}
/**
* Returns the Laminas\Mime\Mime object in use by the message
*
* If the object was not present, it is created and returned. Can be used to
* determine the boundary used in this message.
*
* @return \Laminas\Mime\Mime
*/
public function getMime()
{
if ($this->mime === null) {
$this->mime = new Mime();
}
return $this->mime;
}
/**
* Generate MIME-compliant message from the current configuration
*
* This can be a multipart message if more than one MIME part was added. If
* only one part is present, the content of this part is returned. If no
* part had been added, an empty string is returned.
*
* Parts are separated by the mime boundary as defined in Laminas\Mime\Mime. If
* {@link setMime()} has been called before this method, the Laminas\Mime\Mime
* object set by this call will be used. Otherwise, a new Laminas\Mime\Mime object
* is generated and used.
*
* @param string $EOL EOL string; defaults to {@link Laminas\Mime\Mime::LINEEND}
* @return string
*/
public function generateMessage($EOL = Mime::LINEEND)
{
if (! $this->isMultiPart()) {
if (empty($this->parts)) {
return '';
}
$part = current($this->parts);
$body = $part->getContent($EOL);
} else {
$mime = $this->getMime();
$boundaryLine = $mime->boundaryLine($EOL);
$body = 'This is a message in Mime Format. If you see this, '
. "your mail reader does not support this format." . $EOL;
foreach (array_keys($this->parts) as $p) {
$body .= $boundaryLine
. $this->getPartHeaders($p, $EOL)
. $EOL
. $this->getPartContent($p, $EOL);
}
$body .= $mime->mimeEnd($EOL);
}
return trim($body);
}
/**
* Get the headers of a given part as an array
*
* @param int $partnum
* @return array
*/
public function getPartHeadersArray($partnum)
{
return $this->parts[$partnum]->getHeadersArray();
}
/**
* Get the headers of a given part as a string
*
* @param int $partnum
* @param string $EOL
* @return string
*/
public function getPartHeaders($partnum, $EOL = Mime::LINEEND)
{
return $this->parts[$partnum]->getHeaders($EOL);
}
/**
* Get the (encoded) content of a given part as a string
*
* @param int $partnum
* @param string $EOL
* @return string
*/
public function getPartContent($partnum, $EOL = Mime::LINEEND)
{
return $this->parts[$partnum]->getContent($EOL);
}
/**
* Explode MIME multipart string into separate parts
*
* Parts consist of the header and the body of each MIME part.
*
* @param string $body
* @param string $boundary
* @throws Exception\RuntimeException
* @return array
*/
// @codingStandardsIgnoreStart
protected static function _disassembleMime($body, $boundary)
{
// @codingStandardsIgnoreEnd
$start = 0;
$res = [];
// find every mime part limiter and cut out the
// string before it.
// the part before the first boundary string is discarded:
$p = strpos($body, '--' . $boundary."\n", $start);
if ($p === false) {
// no parts found!
return [];
}
// position after first boundary line
$start = $p + 3 + strlen($boundary);
while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) {
$res[] = substr($body, $start, $p - $start);
$start = $p + 3 + strlen($boundary);
}
// no more parts, find end boundary
$p = strpos($body, '--' . $boundary . '--', $start);
if ($p === false) {
throw new Exception\RuntimeException('Not a valid Mime Message: End Missing');
}
// the remaining part also needs to be parsed:
$res[] = substr($body, $start, $p - $start);
return $res;
}
/**
* Decodes a MIME encoded string and returns a Laminas\Mime\Message object with
* all the MIME parts set according to the given string
*
* @param string $message
* @param string $boundary Multipart boundary; if omitted, $message will be
* treated as a single part.
* @param string $EOL EOL string; defaults to {@link Laminas\Mime\Mime::LINEEND}
* @throws Exception\RuntimeException
* @return Message
*/
public static function createFromMessage($message, $boundary = null, $EOL = Mime::LINEEND)
{
if ($boundary) {
$parts = Decode::splitMessageStruct($message, $boundary, $EOL);
} else {
Decode::splitMessage($message, $headers, $body, $EOL);
$parts = [[
'header' => $headers,
'body' => $body,
]];
}
$res = new static();
foreach ($parts as $part) {
// now we build a new MimePart for the current Message Part:
$properties = [];
foreach ($part['header'] as $header) {
/** @var \Laminas\Mail\Header\HeaderInterface $header */
/**
* @todo check for characterset and filename
*/
$fieldName = $header->getFieldName();
$fieldValue = $header->getFieldValue();
switch (strtolower($fieldName)) {
case 'content-type':
$properties['type'] = $fieldValue;
break;
case 'content-transfer-encoding':
$properties['encoding'] = $fieldValue;
break;
case 'content-id':
$properties['id'] = trim($fieldValue, '<>');
break;
case 'content-disposition':
$properties['disposition'] = $fieldValue;
break;
case 'content-description':
$properties['description'] = $fieldValue;
break;
case 'content-location':
$properties['location'] = $fieldValue;
break;
case 'content-language':
$properties['language'] = $fieldValue;
break;
default:
// Ignore unknown header
break;
}
}
$body = $part['body'];
if (isset($properties['encoding'])) {
switch ($properties['encoding']) {
case 'quoted-printable':
$body = quoted_printable_decode($body);
break;
case 'base64':
$body = base64_decode($body);
break;
}
}
$newPart = new Part($body);
foreach ($properties as $key => $value) {
$newPart->$key = $value;
}
$res->addPart($newPart);
}
return $res;
}
}

398
vendor/laminas/laminas-mime/src/Mime.php vendored Normal file
View File

@ -0,0 +1,398 @@
<?php
/**
* @see https://github.com/laminas/laminas-mime for the canonical source repository
* @copyright https://github.com/laminas/laminas-mime/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-mime/blob/master/LICENSE.md New BSD License
*/
namespace Laminas\Mime;
/**
* Support class for MultiPart Mime Messages
*/
class Mime
{
// @codingStandardsIgnoreStart
const TYPE_OCTETSTREAM = 'application/octet-stream';
const TYPE_TEXT = 'text/plain';
const TYPE_HTML = 'text/html';
const ENCODING_7BIT = '7bit';
const ENCODING_8BIT = '8bit';
const ENCODING_QUOTEDPRINTABLE = 'quoted-printable';
const ENCODING_BASE64 = 'base64';
const DISPOSITION_ATTACHMENT = 'attachment';
const DISPOSITION_INLINE = 'inline';
const LINELENGTH = 72;
const LINEEND = "\n";
const MULTIPART_ALTERNATIVE = 'multipart/alternative';
const MULTIPART_MIXED = 'multipart/mixed';
const MULTIPART_RELATED = 'multipart/related';
const CHARSET_REGEX = '#=\?(?P<charset>[\x21\x23-\x26\x2a\x2b\x2d\x5e\5f\60\x7b-\x7ea-zA-Z0-9]+)\?(?P<encoding>[\x21\x23-\x26\x2a\x2b\x2d\x5e\5f\60\x7b-\x7ea-zA-Z0-9]+)\?(?P<text>[\x21-\x3e\x40-\x7e]+)#';
// @codingStandardsIgnoreEnd
protected $boundary;
protected static $makeUnique = 0;
// lookup-Tables for QuotedPrintable
public static $qpKeys = [
"\x00","\x01","\x02","\x03","\x04","\x05","\x06","\x07",
"\x08","\x09","\x0A","\x0B","\x0C","\x0D","\x0E","\x0F",
"\x10","\x11","\x12","\x13","\x14","\x15","\x16","\x17",
"\x18","\x19","\x1A","\x1B","\x1C","\x1D","\x1E","\x1F",
"\x7F","\x80","\x81","\x82","\x83","\x84","\x85","\x86",
"\x87","\x88","\x89","\x8A","\x8B","\x8C","\x8D","\x8E",
"\x8F","\x90","\x91","\x92","\x93","\x94","\x95","\x96",
"\x97","\x98","\x99","\x9A","\x9B","\x9C","\x9D","\x9E",
"\x9F","\xA0","\xA1","\xA2","\xA3","\xA4","\xA5","\xA6",
"\xA7","\xA8","\xA9","\xAA","\xAB","\xAC","\xAD","\xAE",
"\xAF","\xB0","\xB1","\xB2","\xB3","\xB4","\xB5","\xB6",
"\xB7","\xB8","\xB9","\xBA","\xBB","\xBC","\xBD","\xBE",
"\xBF","\xC0","\xC1","\xC2","\xC3","\xC4","\xC5","\xC6",
"\xC7","\xC8","\xC9","\xCA","\xCB","\xCC","\xCD","\xCE",
"\xCF","\xD0","\xD1","\xD2","\xD3","\xD4","\xD5","\xD6",
"\xD7","\xD8","\xD9","\xDA","\xDB","\xDC","\xDD","\xDE",
"\xDF","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE6",
"\xE7","\xE8","\xE9","\xEA","\xEB","\xEC","\xED","\xEE",
"\xEF","\xF0","\xF1","\xF2","\xF3","\xF4","\xF5","\xF6",
"\xF7","\xF8","\xF9","\xFA","\xFB","\xFC","\xFD","\xFE",
"\xFF"
];
public static $qpReplaceValues = [
"=00","=01","=02","=03","=04","=05","=06","=07",
"=08","=09","=0A","=0B","=0C","=0D","=0E","=0F",
"=10","=11","=12","=13","=14","=15","=16","=17",
"=18","=19","=1A","=1B","=1C","=1D","=1E","=1F",
"=7F","=80","=81","=82","=83","=84","=85","=86",
"=87","=88","=89","=8A","=8B","=8C","=8D","=8E",
"=8F","=90","=91","=92","=93","=94","=95","=96",
"=97","=98","=99","=9A","=9B","=9C","=9D","=9E",
"=9F","=A0","=A1","=A2","=A3","=A4","=A5","=A6",
"=A7","=A8","=A9","=AA","=AB","=AC","=AD","=AE",
"=AF","=B0","=B1","=B2","=B3","=B4","=B5","=B6",
"=B7","=B8","=B9","=BA","=BB","=BC","=BD","=BE",
"=BF","=C0","=C1","=C2","=C3","=C4","=C5","=C6",
"=C7","=C8","=C9","=CA","=CB","=CC","=CD","=CE",
"=CF","=D0","=D1","=D2","=D3","=D4","=D5","=D6",
"=D7","=D8","=D9","=DA","=DB","=DC","=DD","=DE",
"=DF","=E0","=E1","=E2","=E3","=E4","=E5","=E6",
"=E7","=E8","=E9","=EA","=EB","=EC","=ED","=EE",
"=EF","=F0","=F1","=F2","=F3","=F4","=F5","=F6",
"=F7","=F8","=F9","=FA","=FB","=FC","=FD","=FE",
"=FF"
];
// @codingStandardsIgnoreStart
public static $qpKeysString =
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
// @codingStandardsIgnoreEnd
/**
* Check if the given string is "printable"
*
* Checks that a string contains no unprintable characters. If this returns
* false, encode the string for secure delivery.
*
* @param string $str
* @return bool
*/
public static function isPrintable($str)
{
return (strcspn($str, static::$qpKeysString) == strlen($str));
}
/**
* Encode a given string with the QUOTED_PRINTABLE mechanism and wrap the lines.
*
* @param string $str
* @param int $lineLength Defaults to {@link LINELENGTH}
* @param string $lineEnd Defaults to {@link LINEEND}
* @return string
*/
public static function encodeQuotedPrintable(
$str,
$lineLength = self::LINELENGTH,
$lineEnd = self::LINEEND
) {
$out = '';
$str = self::_encodeQuotedPrintable($str);
// Split encoded text into separate lines
$initialPtr = 0;
$strLength = strlen($str);
while ($initialPtr < $strLength) {
$continueAt = $strLength - $initialPtr;
if ($continueAt > $lineLength) {
$continueAt = $lineLength;
}
$chunk = substr($str, $initialPtr, $continueAt);
// Ensure we are not splitting across an encoded character
$endingMarkerPos = strrpos($chunk, '=');
if ($endingMarkerPos !== false && $endingMarkerPos >= strlen($chunk) - 2) {
$chunk = substr($chunk, 0, $endingMarkerPos);
$continueAt = $endingMarkerPos;
}
if (ord($chunk[0]) == 0x2E) { // 0x2E is a dot
$chunk = '=2E' . substr($chunk, 1);
}
// copied from swiftmailer https://git.io/vAXU1
switch (ord(substr($chunk, strlen($chunk) - 1))) {
case 0x09: // Horizontal Tab
$chunk = substr_replace($chunk, '=09', strlen($chunk) - 1, 1);
break;
case 0x20: // Space
$chunk = substr_replace($chunk, '=20', strlen($chunk) - 1, 1);
break;
}
// Add string and continue
$out .= $chunk . '=' . $lineEnd;
$initialPtr += $continueAt;
}
$out = rtrim($out, $lineEnd);
$out = rtrim($out, '=');
return $out;
}
/**
* Converts a string into quoted printable format.
*
* @param string $str
* @return string
*/
// @codingStandardsIgnoreStart
private static function _encodeQuotedPrintable($str)
{
// @codingStandardsIgnoreEnd
$str = str_replace('=', '=3D', $str);
$str = str_replace(static::$qpKeys, static::$qpReplaceValues, $str);
$str = rtrim($str);
return $str;
}
/**
* Encode a given string with the QUOTED_PRINTABLE mechanism for Mail Headers.
*
* Mail headers depend on an extended quoted printable algorithm otherwise
* a range of bugs can occur.
*
* @param string $str
* @param string $charset
* @param int $lineLength Defaults to {@link LINELENGTH}
* @param string $lineEnd Defaults to {@link LINEEND}
* @return string
*/
public static function encodeQuotedPrintableHeader(
$str,
$charset,
$lineLength = self::LINELENGTH,
$lineEnd = self::LINEEND
) {
// Reduce line-length by the length of the required delimiter, charsets and encoding
$prefix = sprintf('=?%s?Q?', $charset);
$lineLength = $lineLength - strlen($prefix) - 3;
$str = self::_encodeQuotedPrintable($str);
// Mail-Header required chars have to be encoded also:
$str = str_replace(['?', ',', ' ', '_'], ['=3F', '=2C', '=20', '=5F'], $str);
// initialize first line, we need it anyways
$lines = [0 => ''];
// Split encoded text into separate lines
$tmp = '';
while (strlen($str) > 0) {
$currentLine = max(count($lines) - 1, 0);
$token = static::getNextQuotedPrintableToken($str);
$substr = substr($str, strlen($token));
$str = (false === $substr) ? '' : $substr;
$tmp .= $token;
if ($token === '=20') {
// only if we have a single char token or space, we can append the
// tempstring it to the current line or start a new line if necessary.
$lineLimitReached = (strlen($lines[$currentLine] . $tmp) > $lineLength);
$noCurrentLine = ($lines[$currentLine] === '');
if ($noCurrentLine && $lineLimitReached) {
$lines[$currentLine] = $tmp;
$lines[$currentLine + 1] = '';
} elseif ($lineLimitReached) {
$lines[$currentLine + 1] = $tmp;
} else {
$lines[$currentLine] .= $tmp;
}
$tmp = '';
}
// don't forget to append the rest to the last line
if (strlen($str) === 0) {
$lines[$currentLine] .= $tmp;
}
}
// assemble the lines together by pre- and appending delimiters, charset, encoding.
for ($i = 0, $count = count($lines); $i < $count; $i++) {
$lines[$i] = " " . $prefix . $lines[$i] . "?=";
}
$str = trim(implode($lineEnd, $lines));
return $str;
}
/**
* Retrieves the first token from a quoted printable string.
*
* @param string $str
* @return string
*/
private static function getNextQuotedPrintableToken($str)
{
if (0 === strpos($str, '=')) {
$token = substr($str, 0, 3);
} else {
$token = substr($str, 0, 1);
}
return $token;
}
/**
* Encode a given string in mail header compatible base64 encoding.
*
* @param string $str
* @param string $charset
* @param int $lineLength Defaults to {@link LINELENGTH}
* @param string $lineEnd Defaults to {@link LINEEND}
* @return string
*/
public static function encodeBase64Header(
$str,
$charset,
$lineLength = self::LINELENGTH,
$lineEnd = self::LINEEND
) {
$prefix = '=?' . $charset . '?B?';
$suffix = '?=';
$remainingLength = $lineLength - strlen($prefix) - strlen($suffix);
$encodedValue = static::encodeBase64($str, $remainingLength, $lineEnd);
$encodedValue = str_replace($lineEnd, $suffix . $lineEnd . ' ' . $prefix, $encodedValue);
$encodedValue = $prefix . $encodedValue . $suffix;
return $encodedValue;
}
/**
* Encode a given string in base64 encoding and break lines
* according to the maximum linelength.
*
* @param string $str
* @param int $lineLength Defaults to {@link LINELENGTH}
* @param string $lineEnd Defaults to {@link LINEEND}
* @return string
*/
public static function encodeBase64(
$str,
$lineLength = self::LINELENGTH,
$lineEnd = self::LINEEND
) {
$lineLength = $lineLength - ($lineLength % 4);
return rtrim(chunk_split(base64_encode($str), $lineLength, $lineEnd));
}
/**
* Constructor
*
* @param null|string $boundary
* @access public
*/
public function __construct($boundary = null)
{
// This string needs to be somewhat unique
if ($boundary === null) {
$this->boundary = '=_' . md5(microtime(1) . static::$makeUnique++);
} else {
$this->boundary = $boundary;
}
}
/**
* Encode the given string with the given encoding.
*
* @param string $str
* @param string $encoding
* @param string $EOL EOL string; defaults to {@link LINEEND}
* @return string
*/
public static function encode($str, $encoding, $EOL = self::LINEEND)
{
switch ($encoding) {
case self::ENCODING_BASE64:
return static::encodeBase64($str, self::LINELENGTH, $EOL);
case self::ENCODING_QUOTEDPRINTABLE:
return static::encodeQuotedPrintable($str, self::LINELENGTH, $EOL);
default:
/**
* @todo 7Bit and 8Bit is currently handled the same way.
*/
return $str;
}
}
/**
* Return a MIME boundary
*
* @access public
* @return string
*/
public function boundary()
{
return $this->boundary;
}
/**
* Return a MIME boundary line
*
* @param string $EOL Defaults to {@link LINEEND}
* @access public
* @return string
*/
public function boundaryLine($EOL = self::LINEEND)
{
return $EOL . '--' . $this->boundary . $EOL;
}
/**
* Return MIME ending
*
* @param string $EOL Defaults to {@link LINEEND}
* @access public
* @return string
*/
public function mimeEnd($EOL = self::LINEEND)
{
return $EOL . '--' . $this->boundary . '--' . $EOL;
}
/**
* Detect MIME charset
*
* Extract parts according to https://tools.ietf.org/html/rfc2047#section-2
*
* @param string $str
* @return string
*/
public static function mimeDetectCharset($str)
{
if (preg_match(self::CHARSET_REGEX, $str, $matches)) {
return strtoupper($matches['charset']);
}
return 'ASCII';
}
}

483
vendor/laminas/laminas-mime/src/Part.php vendored Normal file
View File

@ -0,0 +1,483 @@
<?php
/**
* @see https://github.com/laminas/laminas-mime for the canonical source repository
* @copyright https://github.com/laminas/laminas-mime/blob/master/COPYRIGHT.md
* @license https://github.com/laminas/laminas-mime/blob/master/LICENSE.md New BSD License
*/
namespace Laminas\Mime;
/**
* Class representing a MIME part.
*/
class Part
{
public $type = Mime::TYPE_OCTETSTREAM;
public $encoding = Mime::ENCODING_8BIT;
public $id;
public $disposition;
public $filename;
public $description;
public $charset;
public $boundary;
public $location;
public $language;
protected $content;
protected $isStream = false;
protected $filters = [];
/**
* create a new Mime Part.
* The (unencoded) content of the Part as passed
* as a string or stream
*
* @param mixed $content String or Stream containing the content
* @throws Exception\InvalidArgumentException
*/
public function __construct($content = '')
{
$this->setContent($content);
}
/**
* @todo error checking for setting $type
* @todo error checking for setting $encoding
*/
/**
* Set type
* @param string $type
* @return self
*/
public function setType($type = Mime::TYPE_OCTETSTREAM)
{
$this->type = $type;
return $this;
}
/**
* Get type
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* Set encoding
* @param string $encoding
* @return self
*/
public function setEncoding($encoding = Mime::ENCODING_8BIT)
{
$this->encoding = $encoding;
return $this;
}
/**
* Get encoding
* @return string
*/
public function getEncoding()
{
return $this->encoding;
}
/**
* Set id
* @param string $id
* @return self
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get id
* @return string
*/
public function getId()
{
return $this->id;
}
/**
* Set disposition
* @param string $disposition
* @return self
*/
public function setDisposition($disposition)
{
$this->disposition = $disposition;
return $this;
}
/**
* Get disposition
* @return string
*/
public function getDisposition()
{
return $this->disposition;
}
/**
* Set description
* @param string $description
* @return self
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set filename
* @param string $fileName
* @return self
*/
public function setFileName($fileName)
{
$this->filename = $fileName;
return $this;
}
/**
* Get filename
* @return string
*/
public function getFileName()
{
return $this->filename;
}
/**
* Set charset
* @param string $type
* @return self
*/
public function setCharset($charset)
{
$this->charset = $charset;
return $this;
}
/**
* Get charset
* @return string
*/
public function getCharset()
{
return $this->charset;
}
/**
* Set boundary
* @param string $boundary
* @return self
*/
public function setBoundary($boundary)
{
$this->boundary = $boundary;
return $this;
}
/**
* Get boundary
* @return string
*/
public function getBoundary()
{
return $this->boundary;
}
/**
* Set location
* @param string $location
* @return self
*/
public function setLocation($location)
{
$this->location = $location;
return $this;
}
/**
* Get location
* @return string
*/
public function getLocation()
{
return $this->location;
}
/**
* Set language
* @param string $language
* @return self
*/
public function setLanguage($language)
{
$this->language = $language;
return $this;
}
/**
* Get language
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* Set content
* @param mixed $content String or Stream containing the content
* @throws Exception\InvalidArgumentException
* @return self
*/
public function setContent($content)
{
if (! is_string($content) && ! is_resource($content)) {
throw new Exception\InvalidArgumentException(sprintf(
'Content must be string or resource; received "%s"',
is_object($content) ? get_class($content) : gettype($content)
));
}
$this->content = $content;
if (is_resource($content)) {
$this->isStream = true;
}
return $this;
}
/**
* Set isStream
* @param bool $isStream
* @return self
*/
public function setIsStream($isStream = false)
{
$this->isStream = (bool) $isStream;
return $this;
}
/**
* Get isStream
* @return bool
*/
public function getIsStream()
{
return $this->isStream;
}
/**
* Set filters
* @param array $filters
* @return self
*/
public function setFilters($filters = [])
{
$this->filters = $filters;
return $this;
}
/**
* Get Filters
* @return array
*/
public function getFilters()
{
return $this->filters;
}
/**
* check if this part can be read as a stream.
* if true, getEncodedStream can be called, otherwise
* only getContent can be used to fetch the encoded
* content of the part
*
* @return bool
*/
public function isStream()
{
return $this->isStream;
}
/**
* if this was created with a stream, return a filtered stream for
* reading the content. very useful for large file attachments.
*
* @param string $EOL
* @return resource
* @throws Exception\RuntimeException if not a stream or unable to append filter
*/
public function getEncodedStream($EOL = Mime::LINEEND)
{
if (! $this->isStream) {
throw new Exception\RuntimeException('Attempt to get a stream from a string part');
}
//stream_filter_remove(); // ??? is that right?
switch ($this->encoding) {
case Mime::ENCODING_QUOTEDPRINTABLE:
if (array_key_exists(Mime::ENCODING_QUOTEDPRINTABLE, $this->filters)) {
stream_filter_remove($this->filters[Mime::ENCODING_QUOTEDPRINTABLE]);
}
$filter = stream_filter_append(
$this->content,
'convert.quoted-printable-encode',
STREAM_FILTER_READ,
[
'line-length' => 76,
'line-break-chars' => $EOL
]
);
$this->filters[Mime::ENCODING_QUOTEDPRINTABLE] = $filter;
if (! is_resource($filter)) {
throw new Exception\RuntimeException('Failed to append quoted-printable filter');
}
break;
case Mime::ENCODING_BASE64:
if (array_key_exists(Mime::ENCODING_BASE64, $this->filters)) {
stream_filter_remove($this->filters[Mime::ENCODING_BASE64]);
}
$filter = stream_filter_append(
$this->content,
'convert.base64-encode',
STREAM_FILTER_READ,
[
'line-length' => 76,
'line-break-chars' => $EOL
]
);
$this->filters[Mime::ENCODING_BASE64] = $filter;
if (! is_resource($filter)) {
throw new Exception\RuntimeException('Failed to append base64 filter');
}
break;
default:
}
return $this->content;
}
/**
* Get the Content of the current Mime Part in the given encoding.
*
* @param string $EOL
* @return string
*/
public function getContent($EOL = Mime::LINEEND)
{
if ($this->isStream) {
$encodedStream = $this->getEncodedStream($EOL);
$encodedStreamContents = stream_get_contents($encodedStream);
$streamMetaData = stream_get_meta_data($encodedStream);
if (isset($streamMetaData['seekable']) && $streamMetaData['seekable']) {
rewind($encodedStream);
}
return $encodedStreamContents;
}
return Mime::encode($this->content, $this->encoding, $EOL);
}
/**
* Get the RAW unencoded content from this part
* @return string
*/
public function getRawContent()
{
if ($this->isStream) {
return stream_get_contents($this->content);
}
return $this->content;
}
/**
* Create and return the array of headers for this MIME part
*
* @access public
* @param string $EOL
* @return array
*/
public function getHeadersArray($EOL = Mime::LINEEND)
{
$headers = [];
$contentType = $this->type;
if ($this->charset) {
$contentType .= '; charset=' . $this->charset;
}
if ($this->boundary) {
$contentType .= ';' . $EOL
. " boundary=\"" . $this->boundary . '"';
}
$headers[] = ['Content-Type', $contentType];
if ($this->encoding) {
$headers[] = ['Content-Transfer-Encoding', $this->encoding];
}
if ($this->id) {
$headers[] = ['Content-ID', '<' . $this->id . '>'];
}
if ($this->disposition) {
$disposition = $this->disposition;
if ($this->filename) {
$disposition .= '; filename="' . $this->filename . '"';
}
$headers[] = ['Content-Disposition', $disposition];
}
if ($this->description) {
$headers[] = ['Content-Description', $this->description];
}
if ($this->location) {
$headers[] = ['Content-Location', $this->location];
}
if ($this->language) {
$headers[] = ['Content-Language', $this->language];
}
return $headers;
}
/**
* Return the headers for this part as a string
*
* @param string $EOL
* @return String
*/
public function getHeaders($EOL = Mime::LINEEND)
{
$res = '';
foreach ($this->getHeadersArray($EOL) as $header) {
$res .= $header[0] . ': ' . $header[1] . $EOL;
}
return $res;
}
}