I'm using Plupupload to upload files. if I try to load an exe
with IE9 and the filesize it's over upload_max_filesize
or post_max_size
setting, the uploaded file is corrupt.
This is the PHP script that I am using:
<?php
/**
* upload.php
*
* Copyright 2013, Moxiecode Systems AB
* Released under GPL License.
*
* License: http://www.plupload.com/license
* Contributing: http://www.plupload.com/contributing
*/
// Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
// 5 minutes execution time
@set_time_limit(5 * 60);
// Settings
$targetDir = __DIR__ . DIRECTORY_SEPARATOR . "upload";
// Create target dir
if (!file_exists($targetDir)) {
@mkdir($targetDir);
}
// Get a file name
if (isset($_REQUEST["name"])) {
$fileName = $_REQUEST["name"];
} elseif (!empty($_FILES)) {
$fileName = $_FILES["file"]["name"];
} else {
$fileName = uniqid("file_");
}
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
// Chunking might be enabled
$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
// Open temp file
if (!$out = @fopen("{$filePath}.part", $chunks ? "ab" : "wb")) {
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
}
if (!empty($_FILES)) {
if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
}
// Read binary input stream and append it to temp file
if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
}
} else {
if (!$in = @fopen("php://input", "rb")) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
}
}
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
@fclose($out);
@fclose($in);
// Check if file has been uploaded
if (!$chunks || $chunk == $chunks - 1) {
// Strip the temp .part suffix off
rename("{$filePath}.part", $filePath);
}
// Return Success JSON-RPC response
die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');
upload occurs through the html page:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Plupload - Custom example</title>
<!-- production -->
<script type="text/javascript" src="../js/plupload.full.min.js"></script>
</head>
<body style="font: 13px Verdana; background: #eee; color: #333">
<h1>Custom example</h1>
<p>Shows you how to use the core plupload API.</p>
<div id="filelist">Your browser doesn't have Flash, Silverlight or HTML5 support.</div>
<br />
<div id="container">
<a id="pickfiles" href="javascript:;">[Select files]</a>
<a id="uploadfiles" href="javascript:;">[Upload files]</a>
</div>
<br />
<pre id="console"></pre>
<script type="text/javascript">
// Custom example logic
var uploader = new plupload.Uploader({
runtimes : 'html5,flash,silverlight,html4',
browse_button : 'pickfiles', // you can pass in id...
container: document.getElementById('container'), // ... or DOM Element itself
url : 'upload.php',
flash_swf_url : '../js/Moxie.swf',
silverlight_xap_url : '../js/Moxie.xap',
chunk_size : '2mb',
filters : {
max_file_size : '100mb',
mime_types: [
{title : "Image files", extensions : "jpg,gif,png"},
{title : "Zip files", extensions : "zip"},
{title : "Exe files", extensions : "exe"}
]
},
init: {
PostInit: function() {
document.getElementById('filelist').innerHTML = '';
document.getElementById('uploadfiles').onclick = function() {
uploader.start();
return false;
};
},
FilesAdded: function(up, files) {
plupload.each(files, function(file) {
document.getElementById('filelist').innerHTML += '<div id="' + file.id + '">' + file.name + ' (' + plupload.formatSize(file.size) + ') <b></b></div>';
});
},
UploadProgress: function(up, file) {
document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '<span>' + file.percent + "%</span>";
},
Error: function(up, err) {
document.getElementById('console').innerHTML += "\nError #" + err.code + ": " + err.message;
}
}
});
uploader.init();
</script>
</body>
</html>
When the exe
are corrupt, if I try to open them with notepad++
, I find:
My setting:
PHP Version 5.5.9
System Windows NT PC-XXX 6.0 build 6002 (Windows Vista Service Pack 2) i586
Compiler MSVC11 (Visual C++ 2012)
Architecture x86
Server API Apache 2.0 Handler
php.ini
max_execution_time=30
max_input_time=60
memory_limit=128M
max_file_uploads=20
Additional info
TRY ISSUE YOURSELF
I have created a package for anyone who wants to try.
Download package: http://www.sndesign.it/shared/stackoverflow/plupload-2.1.2.zip
My plupload-2.1.2.zip
also contains a corrupted upload file in plupload-2.1.2/examples/upload/file_54c4c1d05c2ef
folder and the file to try to upload plupload-2.1.2/examples/TryMe.exe
Prepare for the test (I use XAMPP Version 1.8.3):
plupload-2.1.2.zip
in your htdocs
php.ini
upload_max_filesize=22M
post_max_size=22M
(less to TryMe.exe file size 23MB), restart Apachehttp://localhost/plupload-2.1.2/examples/custom.html
%YourHtdocs%/plupload-2.1.2/examples/TryMe.exe
and upload%YourHtdocs%/plupload-2.1.2/examples/upload/
and find the uploaded filephp.ini
upload_max_filesize=24M
post_max_size=24M
(up to TryMe.exe file size 23MB), restart Apache%YourHtdocs%/plupload-2.1.2/examples/TryMe.exe
and upload%YourHtdocs%/plupload-2.1.2/examples/upload/
and find the uploaded fileWhat we know is that an intact file is split up into chunks, each of which is prefixed by an HTTP multipart/form-data
header and a Content-Disposition
header. The first one is always stripped correctly, the second one is not.
This leaves us with 3 possibilities:
The cause for any of the above could be destructive filtering by a firewall, antivirus or any other service that for some reason feels the need to go over your network traffic or RAM/file system activity. For 1. it could also be a bug in the browser/JavaScript/Flash/Silverlight/PlUpload engine. For 2. it could theoretically be Apache messing something up, but that is extremely unlikely since it passes the data 1:1 on to PHP. Now for 3. we can't rule out a bug in PHP, but that is too extremely unlikely since PHP is a constant here and the results vary with different browsers. But I can imagine PHP receives the file, saves it together with the second header, then the file locked because some service is filtering it, filtering takes long because the file is untrusted and big, PHP tries to remove the second header but is denied access because the filtering is still going on and at the end you are left with a file with header. The different outcomes with different browsers could be explained by different chunk sizes or simply the browser performance.
Unfortunately, that's all just speculation. Now since Microsoft did their very best to make it as hard as possible to downgrade IE, I'm currently unable to test it with IE9, all I can give you is some debugging instructions:
In your php.ini, set
enable_post_data_reading = Off
this will completely break all POST requests on that server, but it will allow you to read and dump the file upload requests.
In your upload.php, add those two lines before any other code:
file_put_contents('out.txt', print_r(getallheaders(), true).PHP_EOL.substr(file_get_contents('php://input'), 0, 1000), FILE_APPEND);
exit;
Start Apache and upload TryMe.exe with IE9. Next to your upload.php should now be an out.txt file containing all the relevant data about the file upload requests. Please upload that file somewhere and give us a link to it.
By default PHP max upload file size is set to 2MB.
Try updating your php settings (php.ini):
upload_max_filesize = 20M
post_max_size = 22M
More info: http://php.net/manual/en/ini.core.php#ini.upload-max-filesize and http://php.net/manual/en/ini.core.php#ini.post-max-size
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With