public function parseMultipart()
{
start:
if ($this->frozen) {
return;
}
if ($this->state === self::STATE_SEEKBOUNDARY) {
// seek to the nearest boundary
if (($p = $this->search('--' . $this->boundary . "\r\n")) === false) {
return;
}
// we have found the nearest boundary at position $p
if ($p > 0) {
$extra = $this->read($p);
if ($extra !== "\r\n") {
$this->log('parseBody(): SEEKBOUNDARY: got unexpected data before boundary (length = ' . $p . '): ' . Debug::exportBytes($extra));
}
}
$this->drain(mb_orig_strlen($this->boundary) + 4);
// drain
$this->state = self::STATE_HEADERS;
}
if ($this->state === self::STATE_HEADERS) {
// parse the part's headers
$this->curPartDisp = false;
$i = 0;
do {
$l = $this->readline(\EventBuffer::EOL_CRLF);
if ($l === null) {
return;
}
if ($l === '') {
break;
}
$e = explode(':', $l, 2);
$e[0] = strtr(strtoupper($e[0]), Generic::$htr);
if (isset($e[1])) {
$e[1] = ltrim($e[1]);
}
if ($e[0] === 'CONTENT_DISPOSITION' && isset($e[1])) {
Generic::parseStr($e[1], $this->curPartDisp, true);
if (!isset($this->curPartDisp['form-data'])) {
break;
}
if (!isset($this->curPartDisp['name'])) {
break;
}
$this->curPartDisp['name'] = trim($this->curPartDisp['name'], '"');
$name = $this->curPartDisp['name'];
if (isset($this->curPartDisp['filename'])) {
$this->curPartDisp['filename'] = trim($this->curPartDisp['filename'], '"');
if (!ini_get('file_uploads')) {
break;
}
$this->req->attrs->files[$name] = ['name' => $this->curPartDisp['filename'], 'type' => '', 'tmp_name' => null, 'fp' => null, 'error' => UPLOAD_ERR_OK, 'size' => 0];
$this->curPart =& $this->req->attrs->files[$name];
$this->req->onUploadFileStart($this);
$this->state = self::STATE_UPLOAD;
} else {
$this->curPart =& $this->req->attrs->post[$name];
$this->curPart = '';
}
} elseif ($e[0] === 'CONTENT_TYPE' && isset($e[1])) {
if (isset($this->curPartDisp['name']) && isset($this->curPartDisp['filename'])) {
$this->curPart['type'] = $e[1];
}
}
} while ($i++ < 10);
if ($this->state === self::STATE_HEADERS) {
$this->state = self::STATE_BODY;
}
goto start;
}
if ($this->state === self::STATE_BODY || $this->state === self::STATE_UPLOAD) {
// process the body
$chunkEnd1 = $this->search("\r\n--" . $this->boundary . "\r\n");
$chunkEnd2 = $this->search("\r\n--" . $this->boundary . "--\r\n");
if ($chunkEnd1 === false && $chunkEnd2 === false) {
/* we have only piece of Part in buffer */
$l = $this->length - mb_orig_strlen($this->boundary) - 8;
if ($l <= 0) {
return;
}
if ($this->state === self::STATE_BODY && isset($this->curPartDisp['name'])) {
$this->curPart .= $this->read($l);
} elseif ($this->state === self::STATE_UPLOAD && isset($this->curPartDisp['filename'])) {
$this->curPart['size'] += $l;
if ($this->req->getUploadMaxSize() < $this->curPart['size']) {
$this->curPart['error'] = UPLOAD_ERR_INI_SIZE;
$this->req->header('413 Request Entity Too Large');
$this->req->out('');
$this->req->finish();
} elseif ($this->maxFileSize && $this->maxFileSize < $this->curPart['size']) {
$this->curPart['error'] = UPLOAD_ERR_FORM_SIZE;
$this->req->header('413 Request Entity Too Large');
$this->req->out('');
$this->req->finish();
} else {
$this->curChunkSize = $l;
$this->req->onUploadFileChunk($this);
}
}
} else {
/* we have entire Part in buffer */
if ($chunkEnd1 === false) {
$l = $chunkEnd2;
$endOfMsg = true;
} else {
$l = $chunkEnd1;
$endOfMsg = false;
}
if ($this->state === self::STATE_BODY && isset($this->curPartDisp['name'])) {
$this->curPart .= $this->read($l);
if ($this->curPartDisp['name'] === 'MAX_FILE_SIZE') {
$this->maxFileSize = (int) $this->curPart;
}
} elseif ($this->state === self::STATE_UPLOAD && isset($this->curPartDisp['filename'])) {
$this->curPart['size'] += $l;
$this->curChunkSize = $l;
$this->req->onUploadFileChunk($this, true);
}
$this->state = self::STATE_SEEKBOUNDARY;
if ($endOfMsg) {
// end of whole message
$this->sendEOF();
} else {
goto start;
// let's read the next part
}
}
}
}