4

I am trying to provide retroactive support for a desktop client that posts image data incorrectly to my PHP form.

The problem is that the image data posted hasn't been base64 encoded and I can't change the distributed clients, so I'm wondering if I can do something server side.

The data for the image when written to the server via

file_put_contents($filePath,$_POST['rawImageData']);

Results in a corrupted JPEG (I know that all images uploaded are JPEGs).

The encoding appears to be the default HTTP POST encoding, ISO-8859-1.

I've tried converting the posted data using iconv, utf_decode, mb_convert_encoding and a couple of others with no luck.

Is it possible to recover in this situation?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Dan
  • 367
  • 1
  • 5
  • 17
  • Have you looked at [`mb_convert_encoding()`](http://php.net/manual/en/function.mb-convert-encoding.php) possibly? – Darren Aug 07 '14 at 01:33
  • 1
    Sorry, I should have mentioned that - Yep, I have. – Dan Aug 07 '14 at 02:05
  • 1
    @Darren JPEG image data is binary data, not text. Trying to manipulate it like text, such as by using functions that convert between text encodings, is not going to work properly. –  Aug 07 '14 at 05:18
  • @duskwolf - That's reasonable and makes sense. Is there anyway to convert the text back to binary? – Dan Aug 07 '14 at 21:02
  • Can you post a (hex dump) of an small encoded image so that we can see the encoding. How sure are you that it's ISO-8859-1 and not otherwise corrupted. Can you see the headers that come with the post request? – SpaceDog Aug 13 '14 at 04:23
  • @SpaceDog - Sorry this is the 'smallest' I could find. I did a bintohex to output this data if it matters. EDIT It won't let me add the hex data because it's too big. Can I look for something specific? – Dan Aug 13 '14 at 20:36
  • @Danny, hmmm maybe you could post just the first part (first 32 bytes or so). I would have thought that it should work, unless the file is corrupted. You can see if it looks similar to the jpeg encoding (https://en.wikipedia.org/wiki/JPEG#JPEG_files) you should be able to see the markers. If you hexdump it you should maybe be able to see some text in there. Can you hexdump an original file and the result you get after posting it and compare them? Maybe that will give you a clue. What about the headers from the post request? – SpaceDog Aug 14 '14 at 02:03
  • 1
    Rather than relying on PHP understanding the request and finding the correct POST arguments you might have better success starting with the raw input stream: `file_put_contents($filePath, file_get_contents('php://input'));` and then removing the non-image data yourself – Steve H Aug 15 '14 at 10:00
  • Just a theory, but I know I had this issue once before as well. I managed to get the raw data input `imagecreatefromstring(file_get_contents($uploadedFile['tmp_name']))` then creating a new image from that.`$newImage = imagecreatetruecolor($newWidth, $newHeight);` Even though this is from my resizing script, it should still work the same way, since you're creating a new image file in memory in PHP – Twister1002 Aug 15 '14 at 16:04
  • @Danny You can get the posted image from the form in $_FILES also if i am not understanding it wrong. – Veerendra Aug 19 '14 at 12:36

1 Answers1

6

PHP is probably improperly filling the $_POST array because it is interpreting the JPEG data as form data which might contain & characters or other things that will confuse it. If you manually read the input stream instead of relying on $_POST, you should be able to recover your data.

I wrote some quick code to try to reproduce your issue and test this:

client.php

This code emulates your client; it just reads a JPEG file and writes it raw over a POST request.

$url = 'http://localhost/server.php';
$input = __DIR__ . '/input.jpg';

$parts = parse_url($url);
$sock = fsockopen($parts['host'], 80);

$out = "POST ".$parts['path']." HTTP/1.1\r\n";
$out.= "Host: ".$parts['host']."\r\n";
$out.= "Content-Type: application/x-www-form-urlencoded\r\n";
$out.= "Content-Length: ".(filesize($input) + strlen("rawImageData="))."\r\n";
$out.= "\r\n";
$out.= "rawImageData=";

fwrite($sock, $out);
$fd = fopen($input, 'r');
stream_copy_to_stream($fd, $sock);
fclose($fd);
fclose($sock);

server.php (version 1)

This tries to use the $_POST value as your code does.

$tmp = "/tmp/output.jpg";
file_put_contents($tmp, $_POST['rawImageData']);

server.php (version 2)

This reads the raw input stream from the POST body.

$tmp = "/tmp/output.jpg";
$fd = fopen('php://input', 'r');
fread($fd, strlen("rawImageData=")); //throw this data out
$out = fopen($tmp, 'w');
stream_copy_to_stream($fd, $out);
fclose($out);
fclose($fd);

For input.jpg I used an ~10k JPEG file from Wikipedia.

Result

Server version 1 writes only about 50 bytes to output.jpg, and when I try to open it with a viewer, I get a corrupt JPEG error. Server version 2 writes the full 10k bytes, and my JPEG viewer shows the image just fine.

Austin
  • 2,982
  • 2
  • 28
  • 36