lockfile again
This commit is contained in:
29
vendor/riverline/multipart-parser/.github/workflows/php.yml
vendored
Normal file
29
vendor/riverline/multipart-parser/.github/workflows/php.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: PHP
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
|
||||
name: PHP ${{ matrix.php-versions }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: composer
|
||||
- name: Install vendors
|
||||
run: composer install
|
||||
- name: Run tests
|
||||
run: vendor/bin/phpunit --bootstrap vendor/autoload.php tests
|
||||
19
vendor/riverline/multipart-parser/LICENSE
vendored
Normal file
19
vendor/riverline/multipart-parser/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015-2016 Romain Cambien
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
37
vendor/riverline/multipart-parser/composer.json
vendored
Normal file
37
vendor/riverline/multipart-parser/composer.json
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "riverline/multipart-parser",
|
||||
"description": "One class library to parse multipart content with encoding and charset support.",
|
||||
"keywords": ["http","multipart","parser"],
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Romain Cambien",
|
||||
"email": "romain@cambien.net"
|
||||
},
|
||||
{
|
||||
"name": "Riverline",
|
||||
"homepage": "http://www.riverline.fr"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*",
|
||||
"psr/http-message": "*",
|
||||
"symfony/psr-http-message-bridge": "*",
|
||||
"laminas/laminas-diactoros": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Riverline\\MultiPartParser\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Riverline\\MultiPartParser\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
48
vendor/riverline/multipart-parser/src/Converters/Globals.php
vendored
Normal file
48
vendor/riverline/multipart-parser/src/Converters/Globals.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the MultiPartParser package.
|
||||
*
|
||||
* (c) Romain Cambien <romain@cambien.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Riverline\MultiPartParser\Converters;
|
||||
|
||||
use Riverline\MultiPartParser\StreamedPart;
|
||||
|
||||
/**
|
||||
* Class GlobalsTest
|
||||
*/
|
||||
class Globals
|
||||
{
|
||||
/**
|
||||
* @param bool|resource $input
|
||||
*
|
||||
* @return StreamedPart
|
||||
*/
|
||||
public static function convert($input = STDIN)
|
||||
{
|
||||
$stream = fopen('php://temp', 'rw');
|
||||
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
if (0 === strpos($key, 'HTTP_')) {
|
||||
$key = str_replace('_', '-', strtolower(substr($key, 5)));
|
||||
fwrite($stream, "$key: $value\r\n");
|
||||
} elseif (in_array($key, ['CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE'])) {
|
||||
$key = str_replace('_', '-', strtolower($key));
|
||||
fwrite($stream, "$key: $value\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
fwrite($stream, "\r\n");
|
||||
|
||||
stream_copy_to_stream($input, $stream);
|
||||
|
||||
rewind($stream);
|
||||
|
||||
return new StreamedPart($stream);
|
||||
}
|
||||
}
|
||||
39
vendor/riverline/multipart-parser/src/Converters/HttpFoundation.php
vendored
Normal file
39
vendor/riverline/multipart-parser/src/Converters/HttpFoundation.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the MultiPartParser package.
|
||||
*
|
||||
* (c) Romain Cambien <romain@cambien.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Riverline\MultiPartParser\Converters;
|
||||
|
||||
use Riverline\MultiPartParser\StreamedPart;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class HttpFoundation
|
||||
*/
|
||||
class HttpFoundation
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return StreamedPart
|
||||
*/
|
||||
public static function convert(Request $request)
|
||||
{
|
||||
$stream = fopen('php://temp', 'rw');
|
||||
|
||||
fwrite($stream, (string) $request->headers."\r\n");
|
||||
|
||||
stream_copy_to_stream($request->getContent(true), $stream);
|
||||
|
||||
rewind($stream);
|
||||
|
||||
return new StreamedPart($stream);
|
||||
}
|
||||
}
|
||||
49
vendor/riverline/multipart-parser/src/Converters/PSR7.php
vendored
Normal file
49
vendor/riverline/multipart-parser/src/Converters/PSR7.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the MultiPartParser package.
|
||||
*
|
||||
* (c) Romain Cambien <romain@cambien.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Riverline\MultiPartParser\Converters;
|
||||
|
||||
use Psr\Http\Message\MessageInterface;
|
||||
use Riverline\MultiPartParser\StreamedPart;
|
||||
|
||||
/**
|
||||
* Class PSR7
|
||||
*/
|
||||
class PSR7
|
||||
{
|
||||
/**
|
||||
* @param MessageInterface $message
|
||||
*
|
||||
* @return StreamedPart
|
||||
*/
|
||||
public static function convert(MessageInterface $message)
|
||||
{
|
||||
$stream = fopen('php://temp', 'rw');
|
||||
|
||||
foreach ($message->getHeaders() as $key => $values) {
|
||||
foreach ($values as $value) {
|
||||
fwrite($stream, "$key: $value\r\n");
|
||||
}
|
||||
}
|
||||
fwrite($stream, "\r\n");
|
||||
|
||||
$body = $message->getBody();
|
||||
$body->rewind();
|
||||
|
||||
while (!$body->eof()) {
|
||||
fwrite($stream, $body->read(1024));
|
||||
}
|
||||
|
||||
rewind($stream);
|
||||
|
||||
return new StreamedPart($stream);
|
||||
}
|
||||
}
|
||||
36
vendor/riverline/multipart-parser/src/Part.php
vendored
Normal file
36
vendor/riverline/multipart-parser/src/Part.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the MultiPartParser package.
|
||||
*
|
||||
* (c) Romain Cambien <romain@cambien.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Riverline\MultiPartParser;
|
||||
|
||||
/**
|
||||
* Class Part
|
||||
*
|
||||
* @deprecated Wrapper class, use StreamedPart
|
||||
*/
|
||||
class Part extends StreamedPart
|
||||
{
|
||||
/**
|
||||
* MultiPart constructor.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($content)
|
||||
{
|
||||
$stream = fopen('php://temp', 'rw');
|
||||
fwrite($stream, $content);
|
||||
rewind($stream);
|
||||
|
||||
parent::__construct($stream);
|
||||
}
|
||||
}
|
||||
426
vendor/riverline/multipart-parser/src/StreamedPart.php
vendored
Normal file
426
vendor/riverline/multipart-parser/src/StreamedPart.php
vendored
Normal file
@@ -0,0 +1,426 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the MultiPartParser package.
|
||||
*
|
||||
* (c) Romain Cambien <romain@cambien.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Riverline\MultiPartParser;
|
||||
|
||||
/**
|
||||
* Class StreamedPart
|
||||
*/
|
||||
class StreamedPart
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $bodyOffset;
|
||||
|
||||
/**
|
||||
* @var StreamedPart[]
|
||||
*/
|
||||
private $parts = array();
|
||||
|
||||
/**
|
||||
* StreamParser constructor.
|
||||
*
|
||||
* @param resource $stream
|
||||
*/
|
||||
public function __construct($stream)
|
||||
{
|
||||
if (false === is_resource($stream)) {
|
||||
throw new \InvalidArgumentException('Input is not a stream');
|
||||
}
|
||||
|
||||
$this->stream = $stream;
|
||||
|
||||
// Reset the stream
|
||||
rewind($this->stream);
|
||||
|
||||
// Parse headers
|
||||
$endOfHeaders = false;
|
||||
$bufferSize = 8192;
|
||||
$headerLines = [];
|
||||
$buffer = '';
|
||||
|
||||
while (false !== ($line = fgets($this->stream, $bufferSize))) {
|
||||
// Append to buffer
|
||||
$buffer .= rtrim($line, "\r\n");
|
||||
|
||||
if (strlen($line) === $bufferSize-1) {
|
||||
// EOL not reached, continue
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('' === $buffer) {
|
||||
// Empty line cause by double new line, we reached the end of the headers section
|
||||
$endOfHeaders = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Detect horizontal whitescapes before header
|
||||
$trimmed = ltrim($buffer);
|
||||
if (strlen($buffer) > strlen($trimmed)) {
|
||||
// Multi lines header, append to previous line
|
||||
$headerLines[count($headerLines)-1] .= "\x20".$trimmed;
|
||||
} else {
|
||||
$headerLines[] = $buffer;
|
||||
}
|
||||
|
||||
// Reset buffer
|
||||
$buffer = '';
|
||||
}
|
||||
|
||||
if (false === $endOfHeaders) {
|
||||
throw new \InvalidArgumentException('Content is not valid');
|
||||
}
|
||||
|
||||
$this->headers = [];
|
||||
foreach ($headerLines as $line) {
|
||||
// We don't allow malformed headers that could have a very long length.
|
||||
// Indeed, in HTTP contexts these could be used for DoS/DoW attacks by slowing down the parsing.
|
||||
// Most web server allow a maximum of 8192 characters for an header line, so we'll use that value.
|
||||
if (strlen($line) > 8192) {
|
||||
throw new \InvalidArgumentException('Malformed header: header value is too long');
|
||||
}
|
||||
|
||||
$lineSplit = explode(':', $line, 2);
|
||||
|
||||
if (2 === count($lineSplit)) {
|
||||
list($key, $value) = $lineSplit;
|
||||
// Decode value
|
||||
$value = mb_decode_mimeheader(trim($value));
|
||||
} else {
|
||||
// Bogus header
|
||||
$key = $lineSplit[0];
|
||||
$value = '';
|
||||
}
|
||||
|
||||
// Case-insensitive key
|
||||
$key = strtolower($key);
|
||||
if (false === key_exists($key, $this->headers)) {
|
||||
$this->headers[$key] = $value;
|
||||
} else {
|
||||
// Already got an header with this key, convert to array
|
||||
if (false === is_array($this->headers[$key])) {
|
||||
$this->headers[$key] = (array) $this->headers[$key];
|
||||
}
|
||||
$this->headers[$key][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->bodyOffset = ftell($stream);
|
||||
|
||||
// Is MultiPart ?
|
||||
if ($this->isMultiPart()) {
|
||||
// MultiPart !
|
||||
$boundary = self::getHeaderOption($this->getHeader('Content-Type'), 'boundary');
|
||||
|
||||
if (null === $boundary) {
|
||||
throw new \InvalidArgumentException("Can't find boundary in content type");
|
||||
}
|
||||
|
||||
$separator = '--'.$boundary;
|
||||
|
||||
$partOffset = 0;
|
||||
$endOfBody = false;
|
||||
$eofLength = 0;
|
||||
|
||||
while ($line = fgets($this->stream, $bufferSize)) {
|
||||
$trimmed = rtrim($line, "\r\n");
|
||||
|
||||
// Search the separator
|
||||
if ($trimmed === $separator || $trimmed === $separator.'--') {
|
||||
if ($partOffset > 0) {
|
||||
$currentOffset = ftell($this->stream);
|
||||
$partLength = $currentOffset - $partOffset - strlen($line) - $eofLength;
|
||||
|
||||
// Copy part in a new stream
|
||||
$partStream = fopen('php://temp', 'rw');
|
||||
stream_copy_to_stream($this->stream, $partStream, $partLength, $partOffset);
|
||||
$this->parts[] = new self($partStream);
|
||||
// Reset current stream offset
|
||||
fseek($this->stream, $currentOffset);
|
||||
}
|
||||
|
||||
if ($trimmed === $separator.'--') {
|
||||
// We reach the end separator
|
||||
$endOfBody = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the part offset
|
||||
$partOffset = ftell($this->stream);
|
||||
}
|
||||
|
||||
// Get end of line length (should be 2)
|
||||
$eofLength = strlen($line) - strlen($trimmed);
|
||||
}
|
||||
|
||||
|
||||
if (0 === count($this->parts)
|
||||
|| false === $endOfBody
|
||||
) {
|
||||
throw new \LogicException("Can't find multi-part content");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isMultiPart()
|
||||
{
|
||||
return ('multipart' === mb_strtolower(mb_strstr(
|
||||
self::getHeaderValue($this->getHeader('Content-Type')),
|
||||
'/',
|
||||
true
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @throws \LogicException if is multipart
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
if ($this->isMultiPart()) {
|
||||
throw new \LogicException("MultiPart content, there aren't body");
|
||||
}
|
||||
|
||||
$body = stream_get_contents($this->stream, -1, $this->bodyOffset);
|
||||
|
||||
// Decode
|
||||
$encoding = strtolower((string) $this->getHeader('Content-Transfer-Encoding', '7bit'));
|
||||
switch ($encoding) {
|
||||
case 'base64':
|
||||
$body = base64_decode($body);
|
||||
break;
|
||||
case 'quoted-printable':
|
||||
$body = quoted_printable_decode($body);
|
||||
break;
|
||||
}
|
||||
|
||||
// Convert to UTF-8 ( Not if binary or 7bit ( aka Ascii ) )
|
||||
if (false === in_array($encoding, array('binary', '7bit'))) {
|
||||
// Charset
|
||||
$contentType = $this->getHeader('Content-Type');
|
||||
$charset = self::getHeaderOption($contentType, 'charset');
|
||||
if (null === $charset) {
|
||||
// Try to detect
|
||||
$charset = mb_detect_encoding($body) ?: 'utf-8';
|
||||
}
|
||||
|
||||
// Only convert if not UTF-8
|
||||
if ('utf-8' !== strtolower($charset)) {
|
||||
$body = mb_convert_encoding($body, 'utf-8', $charset);
|
||||
}
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHeader($key, $default = null)
|
||||
{
|
||||
// Case-insensitive key
|
||||
$key = strtolower($key);
|
||||
|
||||
if (false === isset($this->headers[$key])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->headers[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $header
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getHeaderValue($header)
|
||||
{
|
||||
list($value) = self::parseHeaderContent($header);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $header
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getHeaderOptions($header)
|
||||
{
|
||||
list(, $options) = self::parseHeaderContent($header);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $header
|
||||
* @param string $key
|
||||
*
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getHeaderOption($header, $key, $default = null)
|
||||
{
|
||||
$options = self::getHeaderOptions($header);
|
||||
|
||||
if (false === isset($options[$key])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $options[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMimeType()
|
||||
{
|
||||
// Find Content-Disposition
|
||||
$contentType = $this->getHeader('Content-Type');
|
||||
|
||||
return self::getHeaderValue($contentType) ?: 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
// Find Content-Disposition
|
||||
$contentDisposition = $this->getHeader('Content-Disposition');
|
||||
|
||||
return self::getHeaderOption($contentDisposition, 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFileName()
|
||||
{
|
||||
// Find Content-Disposition
|
||||
$contentDisposition = $this->getHeader('Content-Disposition');
|
||||
|
||||
return self::getHeaderOption($contentDisposition, 'filename');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isFile()
|
||||
{
|
||||
return (false === is_null($this->getFileName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StreamedPart[]
|
||||
*
|
||||
* @throws \LogicException if is not multipart
|
||||
*/
|
||||
public function getParts()
|
||||
{
|
||||
if (false === $this->isMultiPart()) {
|
||||
throw new \LogicException("Not MultiPart content, there aren't any parts");
|
||||
}
|
||||
|
||||
return $this->parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Part[]
|
||||
*
|
||||
* @throws \LogicException if is not multipart
|
||||
*/
|
||||
public function getPartsByName($name)
|
||||
{
|
||||
$parts = array();
|
||||
|
||||
foreach ($this->getParts() as $part) {
|
||||
if ($part->getName() === $name) {
|
||||
$parts[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function parseHeaderContent($content)
|
||||
{
|
||||
$parts = explode(';', (string) $content);
|
||||
$headerValue = array_shift($parts);
|
||||
$options = array();
|
||||
// Parse options
|
||||
foreach ($parts as $part) {
|
||||
if (false === empty($part)) {
|
||||
$partSplit = explode('=', $part, 2);
|
||||
if (2 === count($partSplit)) {
|
||||
list ($key, $value) = $partSplit;
|
||||
if ('*' === substr($key, -1)) {
|
||||
// RFC 5987
|
||||
$key = substr($key, 0, -1);
|
||||
if (preg_match(
|
||||
"/(?P<charset>[\w!#$%&+^_`{}~-]+)'(?P<language>[\w-]*)'(?P<value>.*)$/",
|
||||
$value,
|
||||
$matches
|
||||
)) {
|
||||
$value = mb_convert_encoding(
|
||||
rawurldecode($matches['value']),
|
||||
'utf-8',
|
||||
$matches['charset']
|
||||
);
|
||||
}
|
||||
}
|
||||
$options[trim($key)] = trim($value, ' "');
|
||||
} else {
|
||||
// Bogus option
|
||||
$options[$partSplit[0]] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($headerValue, $options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user