<?php
error_reporting(E_ERROR);
$fh = fopen(__FILE__, 'r');
fseek($fh, __COMPILER_HALT_OFFSET__, SEEK_SET);
$mh = new MimeHalt($fh);
var_dump($mh);
eval('?>' . $mh->getFile('/test.php'));
exit;
class MimeHalt {
public $config;
protected $fh;
protected $offset_diff = 0;
protected $offset_after_main_header = 0;
protected $offset_after_first_file_header = 0;
protected $mime_boundary;
public function __construct($fh) {
$this->fh = $fh;
$this->offset_diff = ftell($this->fh);
$this->init();
}
public function init() {
$this->offset_after_first_file_header = 0;
$this->resetOffset();
$first_line = stream_get_line($this->fh, 1048576, "\n");
$matches = array();
if(!preg_match('/^content\-type: multipart\/related; boundary=(.+)(?:;\s+.+)?$/i', $first_line, $matches)) {
throw new RuntimeException('Invalid or corrupt file');
}
$this->mime_boundary = '--' . $matches[1];
$this->resetOffset();
$header = MimeHalt::readNextHeaderChunk($this->fh);
#var_dump(static::processHeader($header));
fseek($this->fh, strlen($this->mime_boundary) + 1, SEEK_CUR);
$this->offset_after_main_header = ftell($this->fh) - $this->offset_diff;
$json_header = MimeHalt::readNextHeaderChunk($this->fh);
$json_body = MimeHalt::readNextBodyChunk($this->fh, $this->mime_boundary);
#var_dump($json_body);
$this->config = json_decode($json_body, true);
$this->offset_after_first_file_header = ftell($this->fh) - $this->offset_diff;
$this->resetOffset();
if(!is_array($this->config['offset_map']) || !count($this->config['offset_map']))
$this->buildOffsetMap();
}
public function resetOffset() {
fseek($this->fh, $this->offset_diff + $this->offset_after_first_file_header, SEEK_SET);
}
public function buildOffsetMap() {
#echo "Starting to rebuild offset map starting at position ", ftell($this->fh), "\n";
fseek($this->fh, $this->offset_diff + $this->offset_after_main_header, SEEK_SET);
$header_text = '';
while($header_text = MimeHalt::readNextHeaderChunk($this->fh)) {
$headers = MimeHalt::processHeader($header_text);
#var_dump([$headers, $header_text]);
$this->config['offset_map'][$headers['Content-ID']] = ftell($this->fh);
MimeHalt::readNextBodyChunk($this->fh, $this->mime_boundary);
}
#var_dump($this->config['offset_map']);
}
public function getFile($filename) {
$content_id = $this->config['files'][$filename];
$offset = $this->config['offset_map'][$content_id];
fseek($this->fh, $offset, SEEK_SET);
return MimeHalt::readNextBodyChunk($this->fh, $this->mime_boundary);
}
protected static function readNextHeaderChunk($fh) {
$line = stream_get_line($fh, 1048576, "\n\n");
if($line === false)
return false;
return $line . "\n";
}
protected static function readNextBodyChunk($fh, $boundary) {
$line = stream_get_line($fh, 1048576, "\n" . $boundary . "\n");
$end_of_body_string = "\n" . $boundary . "--\n";
$eobs_length = strlen($end_of_body_string);
if(substr($line, $eobs_length * -1) == $end_of_body_string)
$line = substr($line, 0, $eobs_length * -1);
return $line;
}
protected static function processHeader($header_text) {
$headers = array();
$matches = array();
$found = preg_match_all('/^(.+): ((.|\r\n\s)+)\r?\n/m', $header_text, $matches, PREG_SET_ORDER);
if($found) {
foreach($matches as $set)
$headers[ $set[1] ] = $set[2];
}
return $headers;
}
}
__halt_compiler();Content-type: multipart/related; boundary=mimehalt_outer
X-MimeHalt-Version: 1.0.0
--mimehalt_outer
Content-Type: application/json
Content-ID: 100000001
{
"files": { "/mimehalt.json": "100000001", "/test.php": "100000002" },
"dirs": [ "/" ],
"offset_map": null
}
--mimehalt_outer
Content-Type: application/x-php
Content-ID: 100000002
<?php
echo "Hello, world!";
?>
--mimehalt_outer--
preferences:
43.27 ms | 402 KiB | 5 Q