���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /home/rickpfrv/wiki.craftaro.com/vendor/wikimedia/shellbox/src/Action/MultipartAction.php
���ѧ٧ѧ�
<?php namespace Shellbox\Action; use GuzzleHttp\Psr7\MultipartStream; use GuzzleHttp\Psr7\Utils; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Shellbox\FileUtils; use Shellbox\Multipart\MultipartReader; use Shellbox\Multipart\MultipartUtils; use Shellbox\Server; use Shellbox\Shellbox; use Shellbox\ShellboxError; use Shellbox\TempDirManager; /** * Base class for actions that share a specific input/output protocol. * * @todo Protocol documentation */ abstract class MultipartAction { /** @var TempDirManager */ protected $tempDirManager; /** @var array */ private $structuredData; /** @var string[] */ private $binaryData; /** @var Server */ private $server; /** @var string[] */ private $headers; /** @var LoggerInterface */ protected $logger; /** @var string[] */ private $inputFiles = []; private const COPY_BUFFER_SIZE = 65536; /** * @param Server $server */ public function __construct( Server $server ) { $this->server = $server; $this->logger = new NullLogger; } /** * @param LoggerInterface $logger */ public function setLogger( LoggerInterface $logger ) { $this->logger = $logger; } /** * The entry point for execution of the action * * @param string[] $pathParts */ public function baseExecute( $pathParts ) { try { $this->tempDirManager = Shellbox::createTempDirManager( $this->getConfig( 'tempDir' ) ); $this->tempDirManager->setLogger( $this->logger ); $this->processInput(); $this->execute( $pathParts ); } finally { if ( $this->tempDirManager ) { $this->tempDirManager->teardown(); } } } /** * Override this to implement the action. * * @param string[] $pathParts */ abstract protected function execute( $pathParts ); /** * Override this to provide the action name as used in the URL. * * @return string */ abstract protected function getActionName(); /** * Get a parameter from the request, or throw if it isn't present * * @param string $name * @return mixed * @throws ShellboxError */ protected function getRequiredParam( $name ) { $nonexistent = new \stdClass; $result = $this->getParam( $name, $nonexistent ); if ( $result === $nonexistent ) { $this->error( "The $name parameter is required" ); } return $result; } /** * Get a parameter from the request. Return the specified default if it * isn't present. * * @param string $name * @param mixed $default * @return mixed */ protected function getParam( $name, $default = null ) { return $this->structuredData[$name] ?? $this->binaryData[$name] ?? $default; } /** * Get a configuration option, or throw if it doesn't exist. * * @param string $name * @return mixed * @throws ShellboxError */ protected function getConfig( $name ) { return $this->server->getConfig( $name ); } /** * Erase a configuration option * * @param string $name */ protected function forgetConfig( $name ) { $this->server->forgetConfig( $name ); } /** * Get a request header of a given name, or null if there was no such header. * * @param string $name * @return string|null */ protected function getHeader( $name ) { if ( $this->headers === null ) { $this->headers = array_change_key_case( getallheaders(), CASE_LOWER ); } return $this->headers[strtolower( $name )] ?? null; } /** * Throw an error exception * * @param string $message * @param int $code * @throws ShellboxError * @return never */ protected function error( $message, $code = 500 ) { throw new ShellboxError( $message, $code ); } /** * Process the request. Read the POST data, do some generic validation, * create input files and populate the parameter arrays. */ private function processInput() { if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) { $this->error( 'The POST method must be used', 405 ); } $auth = $this->getHeader( 'Authorization' ); if ( $auth === null ) { $this->error( 'An Authorization header is required' ); } // Phan doesn't understand functions that always throw, so thinks that // $auth could still be null. Maybe fixable -- see comment in // BlockExitStatusChecker::computeStatusOfCall(). // @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal if ( !preg_match( '/^sha256 ([0-9a-z]+)$/', $auth, $m ) ) { $this->error( 'Invalid Authorization header' ); } $authHash = $m[1]; $ctype = $this->getHeader( 'Content-Type' ); if ( !preg_match( '/^multipart\/mixed/i', $ctype, $m ) ) { $this->error( 'The Content-Type must be multipart/mixed', 415 ); } $boundary = MultipartUtils::extractBoundary( $ctype ); if ( $boundary === false || $boundary === '' ) { $this->error( 'boundary is a required parameter of Content-Type' ); } $inputFile = FileUtils::openInputFile( 'php://input' ); $multipartReader = new MultipartReader( Utils::streamFor( $inputFile ), $boundary, $this->getConfig( 'secretKey' ) ); $preamble = $multipartReader->readPreamble(); if ( trim( $preamble ) !== '' ) { $this->error( 'The multipart preamble must be empty, otherwise a ' . 'fraudulent boundary with a replayed body could be used to send ' . 'unauthorized requests' ); } // phpcs:ignore MediaWiki.ControlStructures.AssignmentInControlStructures while ( ( $headers = $multipartReader->readPartHeaders() ) !== false ) { if ( !isset( $headers['content-disposition'] ) ) { $this->error( 'Part has no Content-Disposition', 400 ); } $disposition = $headers['content-disposition']; if ( $disposition['type'] === 'json-data' ) { $this->structuredData = $multipartReader->readPartAsJson( $headers ); } elseif ( $disposition['type'] === 'form-data' ) { if ( !isset( $disposition['name'] ) ) { $this->error( "multipart form-data requires name" ); } $name = $disposition['name']; $this->binaryData[$name] = $multipartReader->readPartAsString(); } elseif ( $disposition['type'] === 'attachment' ) { $this->processFile( $multipartReader, $headers ); } else { $this->error( "Unknown content disposition type" ); } } $multipartReader->readEpilogue(); if ( $this->getRequiredParam( 'action' ) !== $this->getActionName() ) { $this->error( "The URL action must match the HMAC-verified parameter" ); } if ( !hash_equals( $multipartReader->getHash(), $authHash ) ) { $this->error( "HMAC signature verification failed" ); } } /** * Extract a single file from the MultipartReader * * @param MultipartReader $multipartReader * @param array $headers The part headers */ private function processFile( $multipartReader, $headers ) { if ( !isset( $headers['content-disposition']['filename'] ) ) { $this->error( 'Part has no filename' ); } $fileName = Shellbox::normalizePath( $headers['content-disposition']['filename'] ); $path = $this->tempDirManager->preparePath( $fileName ); $file = FileUtils::openOutputFile( $path ); $multipartReader->copyPartToStream( Utils::streamFor( $file ) ); $this->inputFiles[] = $fileName; } /** * Write a standard result * * @param array $structuredData JSON serializable data * @param string[] $binaryData An array of strings to be sent as multipart * parts * @param string[] $files The names of the output files relative to the * working directory */ protected function writeResult( $structuredData, $binaryData = [], $files = [] ) { $boundary = Shellbox::getUniqueString(); header( "Content-Type: multipart/mixed; boundary=\"$boundary\"" ); $parts = []; $structuredData['log'] = $this->server->flushLogBuffer(); $parts[] = [ 'name' => 'json-data', 'headers' => [ 'Content-Type' => 'application/json', 'Content-Disposition' => 'json-data', ], 'contents' => Shellbox::jsonEncode( $structuredData ) ]; foreach ( $binaryData as $name => $value ) { $parts[] = [ 'name' => $name, 'contents' => $value ]; } foreach ( $files as $name ) { // phpcs:ignore Generic.PHP.NoSilencedErrors $stream = @fopen( $this->tempDirManager->getPath( $name ), 'r' ); if ( $stream ) { $parts[] = [ 'name' => $name, 'headers' => [ 'Content-Type' => 'application/octet-stream', 'Content-Disposition' => "attachment; name=\"$name\"" ], 'contents' => Utils::streamFor( $stream ) ]; } } $multipartStream = new MultipartStream( $parts, $boundary ); while ( !$multipartStream->eof() ) { echo $multipartStream->read( self::COPY_BUFFER_SIZE ); } } /** * Get the names of the received files, relative to the temporary directory * * @return string[] */ protected function getReceivedFileNames() { return $this->inputFiles; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0.38 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�