2

This morning I was woken up by a call from a client running a Concrete5 website, saying that any images uploaded to their website suddenly stopped working. After an hour or two of experimentation and study I realized the root cause of the issue. A couple days ago this client got a new cellphone (A Samsung Epic) which records GPS EXIF data to every image. She was using this cellphone as her camera and for whatever reason if there is any GPS data in the image then Concrete5 explodes. I suspect this is because GPS data is stored as an array rather than a string.

I've been trying to work on a generic function to remove any EXIF data if GPS EXIF data exists. Here's what I have so far (this is located within tools/files/importers):

$fileExtension = strtolower(substr($_FILES['Filedata']['name'], strrpos($_FILES['Filedata']['name'], '.')));
if ($fileExtension == ".jpg" || $fileExtension == ".jpeg"){
    /* File is a JPEG */
    if (exif_read_data($_FILES['Filedata']['tmp_name'], 'GPS') !== false){
        /* File contains GPS EXIF data */
        if (extension_loaded('magickwand') && function_exists("NewMagickWand")) {
            /* ImageMagick is installed and working */
            $img = new Imagick($_FILES['Filedata']['tmp_name']);
            $img->stripImage();
            $img->writeImage($_FILES['Filedata']['tmp_name']);
            $img->clear();
        } elseif (extension_loaded('gd') && function_exists('gd_info')) {
            /* GD is installed and working */
            $img = imagecreatefromjpeg($_FILES['Filedata']['tmp_name']);
            imagejpeg ($img, $_FILES['Filedata']['tmp_name']);
        } else {
            /* Neither ImageMagick nor GD are installed */
            die('No appropriate image handler to remove EXIF data');    
        }
    }
}

Even with this code in place, when I upload an image the EXIF data remains. I have tried placing die() commands throughout the code to figure out exactly which path the code is taking. On this particular server I am never entering the if (extension_loaded('magickwand') && function_exists("NewMagickWand")) block, however I do enter the elseif (extension_loaded('gd') && function_exists('gd_info')) block. Therefore my ImageMagick code may be just fine, but it seems that GD is not removing the EXIF data properly.

Does anyone have experience with ImageMagick/GD or with PHP and EXIF data?

stevendesu
  • 15,753
  • 22
  • 105
  • 182
  • You should send the concrete5 folks some example images that break their software and report the problem so it get's fixed in the main software if you think this is a problem with concrete5. – hakre Jul 22 '11 at 02:25
  • I did. I've been on IRC for the better half of the day but I'm looking to solve this today rather than wait a month for an update. – stevendesu Jul 22 '11 at 02:30
  • Yeah, first needs a fix anyway. – hakre Jul 22 '11 at 02:33

1 Answers1

1

I ran a test on my local machine trying to strip out EXIF data following your method, and it worked~ It did add this comment data: CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 100

What version of gd-jpeg are you using?

Also, have you tried var_dumping your call to imagejpeg? It might be failing~ I've never had great luck writing to the /tmp folder of any server. Maybe you should set the destination file somewhere under your webroot.

Shad
  • 15,134
  • 2
  • 22
  • 34
  • My GD version is `bundled (2.0.34 compatible)`. I tried replacing `imagejpeg ($img, $_FILES['Filedata']['tmp_name']);` with `imagejpeg ($img, "test.jpg");` to see what image was actually being produced (the script which handles imports is never displayed - it's an AJAX uploader) but `test.jpg` was never created. I also tried printing `var_dump($img)` to a file and it was absolutely blank. Should I try `var_dump(imagejpeg ($img))`? – stevendesu Jul 22 '11 at 02:17
  • Quick addition to this - I tried `if($img === false){ fwrite($fp, 'img is false'); }` (of course `$fp` was a file pointer to a file I was using to monitor output) and sure enough `$img` is getting set to false. http://php.net/manual/en/function.imagecreatefromjpeg.php says that false is returned "on errors", but doesn't specify which type of errors would cause this. Any ideas? – stevendesu Jul 22 '11 at 02:29
  • @steven Sounds like write permissions to me. I would recommend copying the file from it's `tmp_name` to somewhere you know you have write permission, and then running your processes on the file you just created (debugging each step along the way); I would continue to verify `$img` is valid resource, and additionally capture the return from the call to `imagejpeg (` as it may be false as well =) – Shad Jul 22 '11 at 05:18
  • I copied `$_FILES['Filedata']['tmp_name']` to `test.jpg` (in a folder where I had rwx permissions) and the file copied just fine, but `imagecreatefromjpeg()` still returned false. I'm certain that so long as `$img` is false, `imagejpeg()` will also return false. Any other ideas? It almost seems like GD is simply broken, but Concrete5 uses GD to generate thumbnails. Could my version of GD be unable to parse images with GPS EXIF data (explaining why C5 fails)? – stevendesu Jul 22 '11 at 12:53
  • @Steven, Gosh... well have you turned error reporting on? `ini_set('display_errors',1)` (And if you had still been doing this via AJAX I would recommend switching to a direct access file so you can see all output.) Your theory sounds the most plausible to me. – Shad Jul 22 '11 at 16:38
  • Switching to a direct access site would be a bit complicated since I'm working with Concrete5, and that would require either changing the `.htaccess` rules to redirect to my temporary test script or it would require changing the entire File Manager. Although I can use `ob_start()`, `ob_get_contents()`, and `ob_end_clean()` to view the output. I'll post any results once I've done this - I have a few other tasks which take priority today. – stevendesu Jul 23 '11 at 19:41