Example #1
0
function phorum_cache_rmdir($path)
{
    if (defined('EVENT_LOGGING')) {
        phorum_mod_event_logging_suspend();
    }
    $stack[] = $path;
    $dirs[] = $path;
    while (count($stack)) {
        $path = array_shift($stack);
        $dir = opendir($path);
        while ($entry = readdir($dir)) {
            if (is_file($path . "/" . $entry)) {
                @unlink($path . "/" . $entry);
            } elseif (is_dir($path . "/" . $entry) && $entry != '.' && $entry != '..') {
                array_unshift($dirs, $path . "/" . $entry);
                $stack[] = $path . "/" . $entry;
            }
        }
        closedir($dir);
    }
    foreach ($dirs as $dir) {
        @rmdir($dir);
    }
    if (defined('EVENT_LOGGING')) {
        phorum_mod_event_logging_resume();
    }
    return;
}
Example #2
0
/**
 * Create an image thumbnail.
 *
 * This function can be used to create a scaled down thumbnail version of
 * an image. You can specifiy a width and/or a height to which to scale
 * down the image. The aspect ratio will always be kept. If both a width
 * and height are provided, then the one which requires the largest scale
 * down will be leading.
 *
 * @param string $image
 *     The raw binary image data.
 *
 * @param integer $max_w
 *     The maximum allowed width for the image in pixels.
 *     Use NULL or 0 (zero) to indicate that any width will do.
 *
 * @param integer $max_h
 *     The maximum allowed height for the image in pixels.
 *     Use NULL or 0 (zero) to indicate that any height will do.
 *
 * @param string $method
 *     The method to use for scaling the image. By default, this function
 *     will try to autodetect a working method. Providing a $method parameter
 *     is mostly useful for debugging purposes. Available methods (in the
 *     order in which they are probed in the code) are:
 *     - gd: using the GD library (requires extension "gd")
 *     - imagick: using the ImageMagick library (requires extension "imagick")
 *     - convert: using the ImageMagick "convert" tool (requires the
 *       ImageMagick package to be installed on the server, does not work
 *       in combination with some PHP safety restrictions).
 *
 * @return mixed
 *     NULL is returned in case creating the thumbnail failed. The function
 *     {@link phorum_api_strerror()} can be used to retrieve information
 *     about the error which occurred.
 *
 *     An array is returned in case creating the thumbnail did work.
 *     This array contains the following fields:
 *     - image:     The scaled down image. NULL if no scaling was needed.
 *     - method:    The method that was used to create the thumbnail.
 *     - cur_w:     The width of the original $image.
 *     - cur_h:     The height of the original $image.
 *     - cur_mime:  The MIME type of the original $image.
 *     - new_w:     The width of the scaled down image.
 *     - new_h:     The height of the scaled down image.
 *     - new_mime:  The MIME type of the scaled down image,
 */
function phorum_api_image_thumbnail($image, $max_w = NULL, $max_h = NULL, $method = NULL)
{
    // Reset error storage.
    $GLOBALS['PHORUM']['API']['errno'] = NULL;
    $GLOBALS['PHORUM']['API']['error'] = NULL;
    $error = NULL;
    if (empty($max_w)) {
        $max_w = NULL;
    }
    if (empty($max_h)) {
        $max_h = NULL;
    }
    // Initialize the return array.
    $img = array('image' => NULL, 'new_mime' => NULL);
    // Check if PHP supports the getimagesize() function. I think it
    // always should, but let's check it to be sure.
    if (!function_exists('getimagesize')) {
        return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'Your PHP installation lacks "getimagesize()" support');
    }
    // Try to determine the image type and size using the getimagesize()
    // PHP function. Unfortunately, this function requires a file on disk
    // to process. Therefore we create a temporary file in the Phorum cache
    // for doing this.
    require_once './include/api/write_file.php';
    $tmpdir = $GLOBALS['PHORUM']['cache'];
    $tmpfile = $tmpdir . '/scale_image_tmp_' . md5($image . microtime());
    if (!phorum_api_write_file($tmpfile, $image)) {
        return NULL;
    }
    // Get the image information and clean up the temporary file.
    $file_info = getimagesize($tmpfile);
    @unlink($tmpfile);
    if ($file_info === FALSE) {
        return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'Running getimagesize() on the image data failed');
    }
    // Add the image data to the return array.
    $img['cur_w'] = $img['new_w'] = $file_info[0];
    $img['cur_h'] = $img['new_h'] = $file_info[1];
    $img['cur_mime'] = $img['new_mime'] = $file_info['mime'];
    // We support only GIF, JPG and PNG images.
    if (substr($img['cur_mime'], 0, 6) == 'image/') {
        $type = substr($img['cur_mime'], 6);
        if ($type != 'jpeg' && $type != 'gif' && $type != 'png') {
            return phorum_api_error_set(PHORUM_ERRNO_ERROR, "Scaling image type \"{$img['cur_mime']}\" is not supported");
        }
    } else {
        return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'The file does not appear to be an image');
    }
    // Check if scaling is required and if yes, what the new size should be.
    // First, determine width and height scale factors.
    $scale_w = NULL;
    $scale_h = NULL;
    // Need horizontal scaling?
    if ($max_w !== NULL && $max_w < $img['cur_w']) {
        $scale_w = $max_w / $img['cur_w'];
    }
    // Need vertical scaling?
    if ($max_h !== NULL && $max_h < $img['cur_h']) {
        $scale_h = $max_h / $img['cur_h'];
    }
    // No scaling needed, return.
    if ($scale_w === NULL && $scale_h === NULL) {
        return $img;
    }
    // The lowest scale factor wins. Compute the required image size.
    if ($scale_h === NULL || $scale_w !== NULL && $scale_w < $scale_h) {
        $img['new_w'] = $max_w;
        $img['new_h'] = floor($img['cur_h'] * $scale_w + 0.5);
    } else {
        $img['new_w'] = floor($img['cur_w'] * $scale_h + 0.5);
        $img['new_h'] = $max_h;
    }
    // -----------------------------------------------------------------
    // Try to use the imagick library tools
    // -----------------------------------------------------------------
    // First, try to load the imagick extension if it is not loaded yet.
    // This way we can make this work on systems where imagick is not built-in
    // or loaded from the PHP ini.
    if (($method === NULL || $method == 'imagick') && !extension_loaded('imagick')) {
        @dl('imagick.so');
    }
    if (($method === NULL || $method == 'imagick') && extension_loaded('imagick') && class_exists('Imagick')) {
        $method = NULL;
        $imagick = new Imagick();
        $imagick->readImageBlob($image);
        $imagick->thumbnailImage($img['new_w'], $img['new_h'], TRUE);
        $imagick->setFormat("jpg");
        $img['image'] = $imagick->getimageblob();
        $img['new_mime'] = 'image/' . $imagick->getFormat();
        $img['method'] = 'imagick';
        return $img;
    }
    // -----------------------------------------------------------------
    // Try to use the GD library tools
    // -----------------------------------------------------------------
    // First, try to load the GD extension if it is not loaded yet.
    // This way we can make this work on systems where gd is not built-in
    // or loaded from the PHP ini.
    if (($method === NULL || $method == 'gd') && !extension_loaded('gd')) {
        @dl('gd.so');
    }
    if (($method === NULL || $method == 'gd') && extension_loaded('gd') && function_exists('gd_info')) {
        // We need gd_info() to check whether GD has the required
        // image support for the type of image that we are handling.
        $gd = gd_info();
        // We need PNG support for the scaled down image.
        if (empty($gd['PNG Support'])) {
            $error = "GD: no PNG support available for creating thumbnail";
        } elseif ($type == 'gif' && empty($gd['GIF Read Support']) || $type == 'jpeg' && empty($gd['JPG Support']) || $type == 'png' && empty($gd['PNG Support'])) {
            $error = "GD: no support available for image type \"{$type}\"";
        } else {
            // Create a GD image handler based on the image data.
            // imagecreatefromstring() spawns PHP warnings if the file cannot
            // be processed. We do not care to see that in the event logging,
            // so if event logging is loaded, we suspend it here.
            // Instead, we catch error output and try to mangle it into a
            // usable error message.
            if (defined('EVENT_LOGGING')) {
                phorum_mod_event_logging_suspend();
            }
            ob_start();
            $original = imagecreatefromstring($image);
            $error = ob_get_contents();
            ob_end_clean();
            $error = trim(preg_replace('!(^(?:\\w+:\\s*).*?:|in /.*$)!', '', trim($error)));
            if (defined('EVENT_LOGGING')) {
                phorum_mod_event_logging_resume();
            }
            if (!$original) {
                if ($error == '') {
                    $error = "GD: Cannot process the {$type} image using GD";
                } else {
                    $error = 'GD: ' . $error;
                }
            } else {
                // Create the scaled image.
                $scaled = imagecreatetruecolor($img['new_w'], $img['new_h']);
                //Retain transparency.
                $trans_idx = imagecolortransparent($original);
                if ($trans_idx >= 0) {
                    $trans = imagecolorsforindex($original, $trans_idx);
                    $idx = imagecolorallocate($scaled, $trans['red'], $trans['green'], $trans['blue']);
                    imagefill($scaled, 0, 0, $idx);
                    imagecolortransparent($scaled, $idx);
                } elseif ($type == 'png') {
                    imagealphablending($scaled, FALSE);
                    $trans = imagecolorallocatealpha($scaled, 0, 0, 0, 127);
                    imagefill($scaled, 0, 0, $trans);
                    imagesavealpha($scaled, TRUE);
                }
                // Scale the image.
                imagecopyresampled($scaled, $original, 0, 0, 0, 0, $img['new_w'], $img['new_h'], $img['cur_w'], $img['cur_h']);
                // Create the jpeg output data for the scaled image.
                ob_start();
                imagejpeg($scaled);
                $image = ob_get_contents();
                $size = ob_get_length();
                ob_end_clean();
                $img['image'] = $image;
                $img['new_mime'] = 'image/jpeg';
                $img['method'] = 'gd';
                return $img;
            }
        }
    }
    // -----------------------------------------------------------------
    // Try to use the ImageMagick "convert" tool
    // -----------------------------------------------------------------
    if ($method === NULL || $method == 'convert') {
        // Try to find the "convert" utility.
        // First, check if it is configured in the Phorum settings.
        $convert = NULL;
        if (isset($PHORUM['imagemagick_convert_path'])) {
            $path = $PHORUM['imagemagick_convert_path'];
            if (is_executable($path)) {
                $convert = $path;
            }
        }
        // Not found? Then simply use "convert" and hope that it
        // can be found in the OS' path.
        if ($convert === NULL) {
            $convert = 'convert';
        }
        // Build the command line.
        $cmd = escapeshellcmd($convert) . ' ' . '- ' . '-thumbnail ' . $img['new_w'] . 'x' . $img['new_h'] . ' ' . '-write jpeg:- ' . '--';
        // Otherwise I get: option requires an argument `-write'
        // Run the command.
        $descriptors = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
        $process = proc_open($cmd, $descriptors, $pipes);
        if ($process == FALSE) {
            $error = 'Failed to run "convert".';
        }
        // Feed convert the image data on STDIN.
        fwrite($pipes[0], $image);
        fclose($pipes[0]);
        // Read the scaled image from STDOUT.
        $scaled = stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        // Read errors.
        $errors = trim(stream_get_contents($pipes[2]));
        fclose($pipes[2]);
        $exit = proc_close($process);
        if ($exit == 0) {
            $img['image'] = $scaled;
            $img['new_mime'] = 'image/jpeg';
            $img['method'] = 'convert';
            return $img;
        }
        // Some error occurred.
        if ($errors == '') {
            $error = 'Got exit code ' . $exit . ' from "convert".';
        } else {
            $error = $errors;
        }
    }
    // -----------------------------------------------------------------
    // Safety nets.
    // -----------------------------------------------------------------
    // Return error if one was set.
    if ($error) {
        return phorum_api_error_set(PHORUM_ERRNO_ERROR, $error);
    }
    // Catch illegal methods
    if ($method !== NULL) {
        return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'Illegal scaling method: ' . $method);
    }
    // If we get here, then we were totally out of luck.
    return phorum_api_error_set(PHORUM_ERRNO_ERROR, 'No working image scaling method found');
}
Example #3
0
/**
 * Clip a part from an image and optionally, resize it.
 *
 * This function can be used to clip a part of an image. You can specifiy
 * width and/or a height to which to scale the clipped image.
 *
 * Below, you see an image of the way in which a clip is defined by the
 * function parameters. $clip_x, $clip_y, $clip_w and $clip_h.
 * <code>
 * +------------------------------------------+
 * |                       ^                  |
 * |                       |                  |
 * |                     clip_y               |
 * |                       |                  |
 * |                       v                  |
 * |            +-------------------+   ^     |
 * |            |                   |   |     |
 * |<--clip_x-->|                   |   |     |
 * |            |      CLIPPED      |   |     |
 * |            |       IMAGE       | clip_h  |
 * |            |                   |   |     |
 * |            |                   |   |     |
 * |            +-------------------+   v     |
 * |                                          |
 * |            <-------clip_w------>         |
 * |                                          |
 * +------------------------------------------+
 * </code>
 *
 * @param string $image
 *     The raw binary image data.
 *
 * @param integer $clip_x
 *     The X-offset for the clip, where 0 indicates the left of the image.
 * @param integer $clip_y
 *     The Y-offset for the clip, where 0 indicates the top of the image.
 * @param integer $clip_w
 *     The width of the clip to take out of the image.
 * @param integer $clip_h
 *     The height of the clip to take out of the image.
 *
 * @param integer $dst_w
 *     The width for the created clip image in pixels.
 *     Use NULL or 0 (zero) to indicate that the width should be
 *     the same as the $clip_w parameter.
 * @param integer $dst_h
 *     The height for the created clip image in pixels.
 *     Use NULL or 0 (zero) to indicate that the height should be
 *     the same as the $clip_h parameter.
 *
 * @param string $method
 *     The method to use for scaling the image. By default, this function
 *     will try to autodetect a working method. Providing a $method parameter
 *     is mostly useful for debugging purposes. Available methods (in the
 *     order in which they are probed in the code) are:
 *     - imagick : using the ImageMagick library (requires extension "imagick")
 *     - gd      : using the GD library (requires extension "gd")
 *     - convert : using the ImageMagick "convert" tool (requires the
 *                 ImageMagick package to be installed on the server and does
 *                 not work in combination with some PHP safety restrictions).
 *
 * @return mixed
 *     FALSE is returned in case creating the clip image failed. The function
 *     {@link phorum_api_error_message()} can be used to retrieve information
 *     about the error that occurred.
 *
 *     An array is returned in case creating the clip image did work.
 *     This array contains the following fields:
 *     - image    : The scaled down image. NULL if no scaling was needed.
 *     - method   : The method that was used to create the thumbnail.
 *     - cur_w    : The width of the original $image.
 *     - cur_h    : The height of the original $image.
 *     - cur_mime : The MIME type of the original $image.
 *     - new_w    : The width of the scaled down image.
 *     - new_h    : The height of the scaled down image.
 *     - new_mime : The MIME type of the scaled down image,
 */
function phorum_api_image_clip($image, $clip_x = 0, $clip_y = 0, $clip_w = NULL, $clip_h = NULL, $dst_w = NULL, $dst_h = NULL, $method = NULL)
{
    global $PHORUM;
    settype($clip_x, 'int');
    settype($clip_y, 'int');
    settype($clip_w, 'int');
    settype($clip_h, 'int');
    settype($dst_w, 'int');
    settype($dst_h, 'int');
    // Reset error storage.
    $PHORUM['API']['errno'] = NULL;
    $PHORUM['API']['error'] = NULL;
    $error = NULL;
    // Initialize the return array.
    $img = array('image' => NULL, 'new_mime' => NULL);
    // Retrieve image info.
    $image_info = phorum_api_image_info($image);
    if ($image_info === FALSE) {
        return FALSE;
    }
    // Derive a name for the image type.
    switch ($image_info['type']) {
        case IMAGETYPE_JPEG:
            $type = 'jpeg';
            break;
        case IMAGETYPE_GIF:
            $type = 'gif';
            break;
        case IMAGETYPE_PNG:
            $type = 'png';
            break;
        default:
            $type = 'unknown';
            // should not occur
    }
    // The clip width and height are inherited from the image
    // width and height, unless they are set.
    if (empty($clip_w)) {
        $clip_w = $image_info['width'] - $clip_x;
    }
    if (empty($clip_h)) {
        $clip_h = $image_info['height'] - $clip_y;
    }
    // The target image width and height are inherited from the clip
    // width and height, unless they are set.
    if (empty($dst_w)) {
        $dst_w = $clip_w;
    }
    if (empty($dst_h)) {
        $dst_h = $clip_h;
    }
    // Add the image data to the return array.
    $img['cur_w'] = $image_info['width'];
    $img['cur_h'] = $image_info['height'];
    $img['new_w'] = $dst_w;
    $img['new_h'] = $dst_h;
    $img['cur_mime'] = $img['new_mime'] = $image_info['mime'];
    // Check if the requested clip fits the source image size.
    if ($clip_x + $clip_w > $img['cur_w']) {
        return phorum_api_error(PHROM_ERRNO_ERROR, "The clip X offset {$clip_x} + clip width {$clip_w} exceeds " . "the source image width {$img['cur_w']}");
    }
    if ($clip_y + $clip_h > $img['cur_h']) {
        return phorum_api_error(PHROM_ERRNO_ERROR, "The clip Y offset {$clip_y} + clip height {$clip_h} exceeds " . "the source image height {$img['cur_h']}");
    }
    // -----------------------------------------------------------------
    // Try to use the imagick library tools
    // -----------------------------------------------------------------
    if (($method === NULL || $method == 'imagick') && extension_loaded('imagick') && class_exists('Imagick')) {
        $method = NULL;
        $imagick = new Imagick();
        $imagick->readImageBlob($image);
        $imagick->flattenImages();
        $tmp = new Imagick();
        $tmp->newPseudoImage($img['cur_w'], $img['cur_h'], 'xc:white');
        $tmp->compositeImage($imagick, imagick::COMPOSITE_OVER, 0, 0);
        $imagick = $tmp;
        $imagick->cropImage($clip_w, $clip_h, $clip_x, $clip_y);
        $imagick->thumbnailImage($dst_w, $dst_h, FALSE);
        $imagick->setImagePage($clip_w, $clip_h, 0, 0);
        $imagick->setFormat("jpg");
        $img['image'] = $imagick->getImagesBlob();
        $img['new_mime'] = 'image/jpeg';
        $img['method'] = 'imagick';
        return $img;
    }
    // -----------------------------------------------------------------
    // Try to use the GD library tools
    // -----------------------------------------------------------------
    if (($method === NULL || $method == 'gd') && extension_loaded('gd') && function_exists('gd_info')) {
        // We need gd_info() to check whether GD has the required
        // image support for the type of image that we are handling.
        $gd = gd_info();
        // We always need JPEG support for the scaled down image.
        if (empty($gd['JPG Support']) && empty($gd['JPEG Support'])) {
            $error = "GD: no JPEG support available for processing images";
        } elseif ($type == 'gif' && empty($gd['GIF Read Support']) || $type == 'jpeg' && (empty($gd['JPG Support']) && empty($gd['JPEG Support'])) || $type == 'png' && empty($gd['PNG Support'])) {
            $error = "GD: no support available for image type \"{$type}\"";
        } else {
            // Create a GD image handler based on the image data.
            // imagecreatefromstring() spawns PHP warnings if the file cannot
            // be processed. We do not care to see that in the event logging,
            // so if event logging is loaded, we suspend it here.
            // Instead, we catch error output and try to mangle it into a
            // usable error message.
            if (defined('EVENT_LOGGING')) {
                phorum_mod_event_logging_suspend();
            }
            ob_start();
            $original = @imagecreatefromstring($image);
            $error = ob_get_contents();
            ob_end_clean();
            $error = trim(preg_replace('!(^(?:\\w+:\\s*).*?:|in /.*$)!', '', trim($error)));
            if (defined('EVENT_LOGGING')) {
                phorum_mod_event_logging_resume();
            }
            if (!$original) {
                if ($error == '') {
                    $error = "GD: Cannot process the {$type} image using GD";
                } else {
                    $error = 'GD: ' . $error;
                }
            } else {
                // Create the scaled image.
                $scaled = imagecreatetruecolor($dst_w, $dst_h);
                // Fill the image to have a background for transparent pixels.
                $white = imagecolorallocate($scaled, 255, 255, 255);
                imagefill($scaled, 0, 0, $white);
                // Scale the image.
                imagecopyresampled($scaled, $original, 0, 0, $clip_x, $clip_y, $dst_w, $dst_h, $clip_w, $clip_h);
                // Create the jpeg output data for the scaled image.
                ob_start();
                imagejpeg($scaled);
                $image = ob_get_contents();
                $size = ob_get_length();
                ob_end_clean();
                imagedestroy($original);
                imagedestroy($scaled);
                $img['image'] = $image;
                $img['new_mime'] = 'image/jpeg';
                $img['method'] = 'gd';
                return $img;
            }
        }
    }
    // -----------------------------------------------------------------
    // Try to use the ImageMagick "convert" tool
    // -----------------------------------------------------------------
    if ($method === NULL || $method == 'convert') {
        // Try to find the "convert" utility.
        // First, check if it is configured in the Phorum settings.
        $convert = NULL;
        if (isset($PHORUM['imagemagick_convert_path'])) {
            $path = $PHORUM['imagemagick_convert_path'];
            if (is_executable($path)) {
                $convert = $path;
            }
        }
        // Not found? Then simply use "convert" and hope that it
        // can be found in the OS' path.
        if ($convert === NULL) {
            $convert = 'convert';
        }
        // Build the command line.
        $cmd = escapeshellcmd($convert) . ' ' . '- ' . '-background white -flatten ' . "-crop {$clip_w}x{$clip_h}+{$clip_x}+{$clip_y} " . '+repage ' . '-thumbnail ' . $dst_w . 'x' . $dst_h . '\\! ' . 'jpeg:-';
        // Run the command.
        $descriptors = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
        $process = proc_open($cmd, $descriptors, $pipes);
        if ($process == FALSE) {
            $error = 'Failed to execute "convert".';
        } else {
            // Feed convert the image data on STDIN.
            fwrite($pipes[0], $image);
            fclose($pipes[0]);
            // Read the scaled image from STDOUT.
            $scaled = stream_get_contents($pipes[1]);
            fclose($pipes[1]);
            // Read errors.
            $errors = trim(stream_get_contents($pipes[2]));
            fclose($pipes[2]);
            $exit = proc_close($process);
            if ($exit == 0) {
                $img['image'] = $scaled;
                $img['new_mime'] = 'image/jpeg';
                $img['method'] = 'convert';
                return $img;
            }
            // Some error occurred.
            if ($errors == '') {
                $error = 'Got exit code ' . $exit . ' from "convert".';
            } else {
                $error = $errors;
            }
        }
    }
    // -----------------------------------------------------------------
    // Safety nets.
    // -----------------------------------------------------------------
    // Return error if one was set.
    if ($error) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, $error);
    }
    // Catch illegal methods
    if ($method !== NULL) {
        return phorum_api_error(PHORUM_ERRNO_ERROR, 'Illegal scaling method: ' . $method);
    }
    // If we get here, then we were totally out of luck.
    return phorum_api_error(PHORUM_ERRNO_ERROR, 'No working image scaling method found');
}