@@ -47,18 +47,32 @@ final class MultipartParser
4747 */
4848 private $ maxInputNestingLevel = 64 ;
4949
50- private $ postCount = 0 ;
50+ /**
51+ * ini setting "upload_max_filesize"
52+ *
53+ * @var int
54+ */
55+ private $ uploadMaxFilesize ;
5156
52- public static function parseRequest (ServerRequestInterface $ request )
53- {
54- $ parser = new self ($ request );
55- return $ parser ->parse ();
56- }
57+ /**
58+ * ini setting "max_file_uploads"
59+ *
60+ * Additionally, setting "file_uploads = off" effectively sets this to zero.
61+ *
62+ * @var int
63+ */
64+ private $ maxFileUploads ;
5765
58- private function __construct ( ServerRequestInterface $ request )
59- {
60- $ this -> request = $ request ;
66+ private $ postCount = 0 ;
67+ private $ filesCount = 0 ;
68+ private $ emptyCount = 0 ;
6169
70+ /**
71+ * @param int|null $uploadMaxFilesize
72+ * @param int|null $maxFileUploads
73+ */
74+ public function __construct ($ uploadMaxFilesize = null , $ maxFileUploads = null )
75+ {
6276 $ var = ini_get ('max_input_vars ' );
6377 if ($ var !== false ) {
6478 $ this ->maxInputVars = (int )$ var ;
@@ -67,18 +81,29 @@ private function __construct(ServerRequestInterface $request)
6781 if ($ var !== false ) {
6882 $ this ->maxInputNestingLevel = (int )$ var ;
6983 }
84+
85+ $ this ->uploadMaxFilesize = $ uploadMaxFilesize === null ? $ this ->iniUploadMaxFilesize () : (int )$ uploadMaxFilesize ;
86+ $ this ->maxFileUploads = $ maxFileUploads === null ? (ini_get ('file_uploads ' ) === '' ? 0 : (int )ini_get ('max_file_uploads ' )) : (int )$ maxFileUploads ;
7087 }
7188
72- private function parse ()
89+ public function parse (ServerRequestInterface $ request )
7390 {
74- $ contentType = $ this -> request ->getHeaderLine ('content-type ' );
91+ $ contentType = $ request ->getHeaderLine ('content-type ' );
7592 if (!preg_match ('/boundary="?(.*)"?$/ ' , $ contentType , $ matches )) {
76- return $ this -> request ;
93+ return $ request ;
7794 }
7895
79- $ this ->parseBody ('-- ' . $ matches [1 ], (string )$ this ->request ->getBody ());
96+ $ this ->request = $ request ;
97+ $ this ->parseBody ('-- ' . $ matches [1 ], (string )$ request ->getBody ());
98+
99+ $ request = $ this ->request ;
100+ $ this ->request = null ;
101+ $ this ->postCount = 0 ;
102+ $ this ->filesCount = 0 ;
103+ $ this ->emptyCount = 0 ;
104+ $ this ->maxFileSize = null ;
80105
81- return $ this -> request ;
106+ return $ request ;
82107 }
83108
84109 private function parseBody ($ boundary , $ buffer )
@@ -137,10 +162,15 @@ private function parsePart($chunk)
137162
138163 private function parseFile ($ name , $ filename , $ contentType , $ contents )
139164 {
165+ $ file = $ this ->parseUploadedFile ($ filename , $ contentType , $ contents );
166+ if ($ file === null ) {
167+ return ;
168+ }
169+
140170 $ this ->request = $ this ->request ->withUploadedFiles ($ this ->extractPost (
141171 $ this ->request ->getUploadedFiles (),
142172 $ name ,
143- $ this -> parseUploadedFile ( $ filename , $ contentType , $ contents )
173+ $ file
144174 ));
145175 }
146176
@@ -150,6 +180,11 @@ private function parseUploadedFile($filename, $contentType, $contents)
150180
151181 // no file selected (zero size and empty filename)
152182 if ($ size === 0 && $ filename === '' ) {
183+ // ignore excessive number of empty file uploads
184+ if (++$ this ->emptyCount + $ this ->filesCount > $ this ->maxInputVars ) {
185+ return ;
186+ }
187+
153188 return new UploadedFile (
154189 Psr7 \stream_for ('' ),
155190 $ size ,
@@ -159,6 +194,22 @@ private function parseUploadedFile($filename, $contentType, $contents)
159194 );
160195 }
161196
197+ // ignore excessive number of file uploads
198+ if (++$ this ->filesCount > $ this ->maxFileUploads ) {
199+ return ;
200+ }
201+
202+ // file exceeds "upload_max_filesize" ini setting
203+ if ($ size > $ this ->uploadMaxFilesize ) {
204+ return new UploadedFile (
205+ Psr7 \stream_for ('' ),
206+ $ size ,
207+ UPLOAD_ERR_INI_SIZE ,
208+ $ filename ,
209+ $ contentType
210+ );
211+ }
212+
162213 // file exceeds MAX_FILE_SIZE value
163214 if ($ this ->maxFileSize !== null && $ size > $ this ->maxFileSize ) {
164215 return new UploadedFile (
@@ -271,4 +322,28 @@ private function extractPost($postFields, $key, $value)
271322
272323 return $ postFields ;
273324 }
325+
326+ /**
327+ * Gets upload_max_filesize from PHP's configuration expressed in bytes
328+ *
329+ * @return int
330+ * @link http://php.net/manual/en/ini.core.php#ini.upload-max-filesize
331+ * @codeCoverageIgnore
332+ */
333+ private function iniUploadMaxFilesize ()
334+ {
335+ $ size = ini_get ('upload_max_filesize ' );
336+ $ suffix = strtoupper (substr ($ size , -1 ));
337+ if ($ suffix === 'K ' ) {
338+ return substr ($ size , 0 , -1 ) * 1024 ;
339+ }
340+ if ($ suffix === 'M ' ) {
341+ return substr ($ size , 0 , -1 ) * 1024 * 1024 ;
342+ }
343+ if ($ suffix === 'G ' ) {
344+ return substr ($ size , 0 , -1 ) * 1024 * 1024 * 1024 ;
345+ }
346+
347+ return $ size ;
348+ }
274349}
0 commit comments