<?php
/**
 * Unique functions for Standard EWWW I.O. plugins.
 *
 * This file contains functions that are unique to the regular EWWW IO plugin.
 *
 * @link https://ewww.io
 * @package EWWW_Image_Optimizer
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Installation routine for PNGOUT.
add_action( 'admin_action_ewww_image_optimizer_install_pngout', 'ewww_image_optimizer_install_pngout_wrapper' );
// Removes the binaries when the plugin is deactivated.
register_deactivation_hook( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, 'ewww_image_optimizer_remove_binaries' );

/**
 * Make sure the cloud constant is defined.
 *
 * Check to see if the cloud constant is defined (which would mean we've already run init) and set it properly if not.
 */
function ewww_image_optimizer_cloud_init() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	// Basically this happens on settings save, so we need to check if the settings are being saved with an empty key, and pre-emptively set the constant to false.
	if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) && isset( $_POST['ewww_image_optimizer_cloud_key'] ) && ! $_POST['ewww_image_optimizer_cloud_key'] ) {
		define( 'EWWW_IMAGE_OPTIMIZER_CLOUD', false );
		return;
	}
	if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) > 10 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 ) {
		define( 'EWWW_IMAGE_OPTIMIZER_CLOUD', true );
	} elseif ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
		define( 'EWWW_IMAGE_OPTIMIZER_CLOUD', false );
	}
	ewwwio_memory( __FUNCTION__ );
}

/**
 * Initializes settings for the local tools, and runs the checks for tools on select pages.
 */
function ewww_image_optimizer_exec_init() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	if ( defined( 'WPE_PLUGIN_VERSION' ) ) {
		add_action( 'network_admin_notices', 'ewww_image_optimizer_notice_wpengine' );
		add_action( 'admin_notices', 'ewww_image_optimizer_notice_wpengine' );
		if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) ) {
			define( 'EWWW_IMAGE_OPTIMIZER_NOEXEC', true );
		}
	}
	// If cloud is fully enabled, we're going to skip all the checks related to the bundled tools.
	if ( EWWW_IMAGE_OPTIMIZER_CLOUD ) {
		ewwwio_debug_message( 'cloud options enabled, shutting off binaries' );
		ewww_image_optimizer_disable_tools();
		// Check if this is an unsupported OS (not Linux or Mac OSX or FreeBSD or Windows or SunOS).
	} elseif ( 'Linux' != PHP_OS && 'Darwin' != PHP_OS && 'FreeBSD' != PHP_OS && 'WINNT' != PHP_OS && 'SunOS' != PHP_OS ) {
		// Call the function to display a notice.
		add_action( 'network_admin_notices', 'ewww_image_optimizer_notice_os' );
		add_action( 'admin_notices', 'ewww_image_optimizer_notice_os' );
		// Turn off all the tools.
		ewwwio_debug_message( 'unsupported OS, disabling tools: ' . PHP_OS );
		ewww_image_optimizer_disable_tools();
	} else {
		add_action( 'load-upload.php', 'ewww_image_optimizer_tool_init', 9 );
		add_action( 'load-media-new.php', 'ewww_image_optimizer_tool_init' );
		add_action( 'load-media_page_ewww-image-optimizer-bulk', 'ewww_image_optimizer_tool_init' );
		add_action( 'load-settings_page_' . str_replace( '.php', '', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ), 'ewww_image_optimizer_tool_init' );
		add_action( 'load-plugins.php', 'ewww_image_optimizer_tool_init' );
		add_action( 'load-ims_gallery_page_ewww-ims-optimize', 'ewww_image_optimizer_tool_init' );
		add_action( 'load-media_page_ewww-image-optimizer-unoptimized', 'ewww_image_optimizer_tool_init' );
		add_action( 'load-flagallery_page_flag-manage-gallery', 'ewww_image_optimizer_tool_init' );
	}
	ewwwio_memory( __FUNCTION__ );
}

/**
 * Check for binary installation and availability.
 */
function ewww_image_optimizer_tool_init() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	// Make sure the bundled tools are installed.
	if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_bundle' ) ) {
		ewww_image_optimizer_install_tools();
	}
	// Check for optimization utilities and register a notice if something is missing.
	add_action( 'network_admin_notices', 'ewww_image_optimizer_notice_utils' );
	add_action( 'admin_notices', 'ewww_image_optimizer_notice_utils' );
	if ( EWWW_IMAGE_OPTIMIZER_CLOUD ) {
		ewwwio_debug_message( 'cloud options enabled, shutting off binaries' );
		ewww_image_optimizer_disable_tools();
	}
}

/**
 * Set some default option values.
 */
function ewww_image_optimizer_set_defaults() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	add_site_option( 'ewww_image_optimizer_disable_pngout', true );
	add_site_option( 'ewww_image_optimizer_optipng_level', 2 );
	add_site_option( 'ewww_image_optimizer_pngout_level', 2 );
	add_site_option( 'ewww_image_optimizer_jpegtran_copy', true );
	add_site_option( 'ewww_image_optimizer_jpg_level', '10' );
	add_site_option( 'ewww_image_optimizer_png_level', '10' );
	add_site_option( 'ewww_image_optimizer_gif_level', '10' );
	add_site_option( 'ewww_image_optimizer_pdf_level', '0' );
}

/**
 * Tells the user they are on an unsupported operating system.
 */
function ewww_image_optimizer_notice_os() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	echo "<div id='ewww-image-optimizer-warning-os' class='error'><p><strong>" . esc_html__( 'EWWW Image Optimizer is supported on Linux, FreeBSD, Mac OSX, and Windows', 'ewww-image-optimizer' ) . '.</strong> ' .
		/* translators: %s: An operating system. */
		sprintf( esc_html__( 'Unfortunately, the EWWW Image Optimizer plugin does not work with %s', 'ewww-image-optimizer' ), htmlentities( PHP_OS ) ) . '.</p></div>';
}

/**
 * Generates the source and destination paths for the executables that we bundle with the plugin.
 *
 * Paths are determined based on the operating system and architecture.
 */
function ewww_image_optimizer_install_paths() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	$tool_path = trailingslashit( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
	if ( PHP_OS == 'WINNT' ) {
		$gifsicle_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'gifsicle.exe';
		$optipng_src  = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'optipng.exe';
		$jpegtran_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'jpegtran.exe';
		$pngquant_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngquant.exe';
		$webp_src     = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'cwebp.exe';
		$gifsicle_dst = $tool_path . 'gifsicle.exe';
		$optipng_dst  = $tool_path . 'optipng.exe';
		$jpegtran_dst = $tool_path . 'jpegtran.exe';
		$pngquant_dst = $tool_path . 'pngquant.exe';
		$webp_dst     = $tool_path . 'cwebp.exe';
	}
	if ( PHP_OS == 'Darwin' ) {
		$gifsicle_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'gifsicle-mac';
		$optipng_src  = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'optipng-mac';
		$jpegtran_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'jpegtran-mac';
		$pngquant_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngquant-mac';
		$webp_src     = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'cwebp-mac12';
		$gifsicle_dst = $tool_path . 'gifsicle';
		$optipng_dst  = $tool_path . 'optipng';
		$jpegtran_dst = $tool_path . 'jpegtran';
		$pngquant_dst = $tool_path . 'pngquant';
		$webp_dst     = $tool_path . 'cwebp';
	}
	if ( PHP_OS == 'SunOS' ) {
		$gifsicle_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'gifsicle-sol';
		$optipng_src  = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'optipng-sol';
		$jpegtran_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'jpegtran-sol';
		$pngquant_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngquant-sol';
		$webp_src     = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'cwebp-sol';
		$gifsicle_dst = $tool_path . 'gifsicle';
		$optipng_dst  = $tool_path . 'optipng';
		$jpegtran_dst = $tool_path . 'jpegtran';
		$pngquant_dst = $tool_path . 'pngquant';
		$webp_dst     = $tool_path . 'cwebp';
	}
	if ( PHP_OS == 'FreeBSD' ) {
		if ( ewww_image_optimizer_function_exists( 'php_uname' ) ) {
			$arch_type = php_uname( 'm' );
			ewwwio_debug_message( "CPU architecture: $arch_type" );
		} else {
			ewwwio_debug_message( 'CPU architecture unknown, php_uname disabled' );
		}
		$gifsicle_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'gifsicle-fbsd';
		$optipng_src  = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'optipng-fbsd';
		$jpegtran_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'jpegtran-fbsd';
		$pngquant_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngquant-fbsd';
		$webp_src     = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'cwebp-fbsd';
		$gifsicle_dst = $tool_path . 'gifsicle';
		$optipng_dst  = $tool_path . 'optipng';
		$jpegtran_dst = $tool_path . 'jpegtran';
		$pngquant_dst = $tool_path . 'pngquant';
		$webp_dst     = $tool_path . 'cwebp';
	}
	if ( PHP_OS == 'Linux' ) {
		if ( ewww_image_optimizer_function_exists( 'php_uname' ) ) {
			$arch_type = php_uname( 'm' );
			ewwwio_debug_message( "CPU architecture: $arch_type" );
		} else {
			ewwwio_debug_message( 'CPU architecture unknown, php_uname disabled' );
		}
		$gifsicle_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'gifsicle-linux';
		$optipng_src  = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'optipng-linux';
		$jpegtran_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'jpegtran-linux';
		$pngquant_src = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngquant-linux';
		$webp_src     = EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'cwebp-linux';
		$gifsicle_dst = $tool_path . 'gifsicle';
		$optipng_dst  = $tool_path . 'optipng';
		$jpegtran_dst = $tool_path . 'jpegtran';
		$pngquant_dst = $tool_path . 'pngquant';
		$webp_dst     = $tool_path . 'cwebp';
	}
	ewwwio_debug_message( "generated paths:<br>$jpegtran_src<br>$optipng_src<br>$gifsicle_src<br>$pngquant_src<br>$webp_src<br>$jpegtran_dst<br>$optipng_dst<br>$gifsicle_dst<br>$pngquant_dst<br>$webp_dst" );
	ewwwio_memory( __FUNCTION__ );
	return array( $jpegtran_src, $optipng_src, $gifsicle_src, $pngquant_src, $webp_src, $jpegtran_dst, $optipng_dst, $gifsicle_dst, $pngquant_dst, $webp_dst );
}

/**
 * Makes sure permissions on a file/folder are adequate.
 *
 * @param string $file The file or folder to test.
 * @param string $minimum The minimum file permissions needed.
 * @return bool True if permissions are equal to or better than what is required.
 */
function ewww_image_optimizer_check_permissions( $file, $minimum ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	$perms = fileperms( $file );
	ewwwio_debug_message( "permissions for $file: " . substr( sprintf( '%o', $perms ), -4 ) );
	if ( ! is_file( $file ) ) {
		ewwwio_debug_message( 'permissions check failed, file not found' );
		return false;
	}
	if ( is_readable( $file ) && is_executable( $file ) ) {
		ewwwio_debug_message( 'permissions ok' );
		return true;
	}
	ewwwio_debug_message( 'permissions insufficient' );
	return false;
}

/**
 * Alert the user when the tool folder could not be created.
 */
function ewww_image_optimizer_tool_folder_notice() {
	echo "<div id='ewww-image-optimizer-warning-tool-folder-create' class='error'><p><strong>" . esc_html__( 'EWWW Image Optimizer could not create the tool folder', 'ewww-image-optimizer' ) . ': ' . htmlentities( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) . '.</strong> ' . esc_html__( 'Please adjust permissions or create the folder', 'ewww-image-optimizer' ) . '.</p></div>';
}

/**
 * Alert the user when permissions on the tool folder are insufficient.
 */
function ewww_image_optimizer_tool_folder_permissions_notice() {
	if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
		// Need to include the plugin library for the is_plugin_active function.
		require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
	}
	if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
		$settings_page = 'settings.php?page=' . EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL;
	} else {
		$settings_page = 'options-general.php?page=' . EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL;
	}
	echo "<div id='ewww-image-optimizer-warning-tool-folder-permissions' class='error'><p><strong>" .
		/* translators: %s: Folder location where executables should be installed */
		sprintf( esc_html__( 'EWWW Image Optimizer could not install tools in %s', 'ewww-image-optimizer' ), htmlentities( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) . '.</strong> ' .
		esc_html__( 'Please adjust permissions or create the folder. If you have installed the tools elsewhere on your system, check the option to Use System Paths.', 'ewww-image-optimizer' ) . ' ' .
		/* translators: 1: Settings Page (link) 2: Installation Instructions (link) */
		sprintf( esc_html__( 'For more details, visit the %1$s or the %2$s.', 'ewww-image-optimizer' ), "<a href='$settings_page'>" . esc_html__( 'Settings Page', 'ewww-image-optimizer' ) . '</a>', "<a href='https://docs.ewww.io/'>" . esc_html__( 'Installation Instructions', 'ewww-image-optimizer' ) . '</a>' ) . '</p></div>';
}

/**
 * Alert the user when tool installation fails.
 */
function ewww_image_optimizer_tool_installation_failed_notice() {
	if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
		// Need to include the plugin library for the is_plugin_active function.
		require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
	}
	if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
		$settings_page = 'settings.php?page=' . EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL;
	} else {
		$settings_page = 'options-general.php?page=' . EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL;
	}
	echo "<div id='ewww-image-optimizer-warning-tool-install' class='error'><p><strong>" .
		/* translators: %s: Folder location where executables should be installed */
		sprintf( esc_html__( 'EWWW Image Optimizer could not install tools in %s', 'ewww-image-optimizer' ), htmlentities( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) . '.</strong> ' .
		esc_html__( 'Please adjust permissions or create the folder. If you have installed the tools elsewhere on your system, check the option to Use System Paths.', 'ewww-image-optimizer' ) . ' ' .
		/* translators: 1: Settings Page (link) 2: Installation Instructions (link) */
		sprintf( esc_html__( 'For more details, visit the %1$s or the %2$s.', 'ewww-image-optimizer' ), "<a href='$settings_page'>" . esc_html__( 'Settings Page', 'ewww-image-optimizer' ) . '</a>', "<a href='https://docs.ewww.io/'>" . esc_html__( 'Installation Instructions', 'ewww-image-optimizer' ) . '</a>' ) . '</p></div>';
}

/**
 * Installs the executables that are bundled with the plugin.
 */
function ewww_image_optimizer_install_tools() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	ewwwio_debug_message( 'Checking/Installing tools in ' . EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
	$toolfail = false;
	if ( ! is_dir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) && is_writable( dirname( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) ) {
		ewwwio_debug_message( 'folder does not exist, creating...' );
		if ( ! mkdir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) {
			ewwwio_debug_message( 'could not create folder' );
			return;
		}
	} elseif ( is_dir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) {
		if ( ! is_writable( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) {
			ewwwio_debug_message( 'wp-content/ewww is not writable, not installing anything' );
			return;
		}
		$ewww_perms = substr( sprintf( '%o', fileperms( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ), -4 );
		ewwwio_debug_message( "wp-content/ewww permissions: $ewww_perms" );
	} else {
		ewwwio_debug_message( 'wp-content is not writable, and the ewww folder does not exist' );
		return;
	}
	list ( $jpegtran_src, $optipng_src, $gifsicle_src, $pngquant_src, $webp_src, $jpegtran_dst, $optipng_dst, $gifsicle_dst, $pngquant_dst, $webp_dst ) = ewww_image_optimizer_install_paths();
	$skip = ewww_image_optimizer_skip_tools();
	if ( ! $skip['jpegtran'] && ( ! file_exists( $jpegtran_dst ) || filesize( $jpegtran_dst ) != filesize( $jpegtran_src ) ) ) {
		ewwwio_debug_message( 'jpegtran not found or different size, installing' );
		if ( ! copy( $jpegtran_src, $jpegtran_dst ) ) {
			$toolfail = true;
			ewwwio_debug_message( 'could not copy jpegtran' );
		}
	}
	if ( ! $skip['gifsicle'] && ( ! file_exists( $gifsicle_dst ) || filesize( $gifsicle_dst ) != filesize( $gifsicle_src ) ) ) {
		ewwwio_debug_message( 'gifsicle not found or different size, installing' );
		if ( ! copy( $gifsicle_src, $gifsicle_dst ) ) {
			$toolfail = true;
			ewwwio_debug_message( 'could not copy gifsicle' );
		}
	}
	if ( ! $skip['optipng'] && ( ! file_exists( $optipng_dst ) || filesize( $optipng_dst ) != filesize( $optipng_src ) ) ) {
		ewwwio_debug_message( 'optipng not found or different size, installing' );
		if ( ! copy( $optipng_src, $optipng_dst ) ) {
			$toolfail = true;
			ewwwio_debug_message( 'could not copy optipng' );
		}
	}
	if ( ! $skip['pngquant'] && ( ! file_exists( $pngquant_dst ) || filesize( $pngquant_dst ) != filesize( $pngquant_src ) ) ) {
		ewwwio_debug_message( 'pngquant not found or different size, installing' );
		if ( ! copy( $pngquant_src, $pngquant_dst ) ) {
			$toolfail = true;
			ewwwio_debug_message( 'could not copy pngquant' );
		}
	}
	if ( ! $skip['webp'] && ( ! file_exists( $webp_dst ) || filesize( $webp_dst ) != filesize( $webp_src ) ) ) {
		ewwwio_debug_message( 'webp not found or different size, installing' );
		if ( ! copy( $webp_src, $webp_dst ) ) {
			$toolfail = true;
			ewwwio_debug_message( 'could not copy webp' );
		}
	}
	// Install special version of cwebp for Mac OSX 10.8 systems.
	if ( PHP_OS == 'Darwin' && ! $skip['webp'] ) {
		$webp8_dst = $webp_dst . '-alt';
		$webp8_src = str_replace( 'mac9', 'mac8', $webp_src );
		if ( ! file_exists( $webp8_dst ) || ( ewww_image_optimizer_md5check( $webp8_dst ) && filesize( $webp8_dst ) != filesize( $webp8_src ) ) ) {
			ewwwio_debug_message( "copying $webp8_src to $webp8_dst" );
			if ( ! copy( $webp8_src, $webp8_dst ) ) {
				// This isn't a fatal error, besides we'll see it in the debug if needed.
				ewwwio_debug_message( 'could not copy OSX 10.8 cwebp to cwebp-alt' );
			}
			if ( ! ewww_image_optimizer_check_permissions( $webp8_dst, 'rwxr-xr-x' ) ) {
				if ( ! is_writable( $webp8_dst ) || ! chmod( $webp8_dst, 0755 ) ) {
					ewwwio_debug_message( 'could not set cwebp8-alt permissions' );
				}
			}
		}
	}

	if ( PHP_OS != 'WINNT' && ! $toolfail ) {
		ewwwio_debug_message( 'Linux/UNIX style OS, checking permissions' );
		if ( ! $skip['jpegtran'] && ! ewww_image_optimizer_check_permissions( $jpegtran_dst, 'rwxr-xr-x' ) ) {
			if ( ! is_writable( $jpegtran_dst ) || ! chmod( $jpegtran_dst, 0755 ) ) {
				$toolfail = true;
				ewwwio_debug_message( 'could not set jpegtran permissions' );
			}
		}
		if ( ! $skip['gifsicle'] && ! ewww_image_optimizer_check_permissions( $gifsicle_dst, 'rwxr-xr-x' ) ) {
			if ( ! is_writable( $gifsicle_dst ) || ! chmod( $gifsicle_dst, 0755 ) ) {
				$toolfail = true;
				ewwwio_debug_message( 'could not set gifsicle permissions' );
			}
		}
		if ( ! $skip['optipng'] && ! ewww_image_optimizer_check_permissions( $optipng_dst, 'rwxr-xr-x' ) ) {
			if ( ! is_writable( $optipng_dst ) || ! chmod( $optipng_dst, 0755 ) ) {
				$toolfail = true;
				ewwwio_debug_message( 'could not set optipng permissions' );
			}
		}
		if ( ! $skip['pngquant'] && ! ewww_image_optimizer_check_permissions( $pngquant_dst, 'rwxr-xr-x' ) ) {
			if ( ! is_writable( $pngquant_dst ) || ! chmod( $pngquant_dst, 0755 ) ) {
				$toolfail = true;
				ewwwio_debug_message( 'could not set pngquant permissions' );
			}
		}
		if ( ! $skip['webp'] && ! ewww_image_optimizer_check_permissions( $webp_dst, 'rwxr-xr-x' ) ) {
			if ( ! is_writable( $webp_dst ) || ! chmod( $webp_dst, 0755 ) ) {
				$toolfail = true;
				ewwwio_debug_message( 'could not set webp permissions' );
			}
		}
	}
	if ( $toolfail ) {
		add_action( 'network_admin_notices', 'ewww_image_optimizer_tool_installation_failed_notice' );
		add_action( 'admin_notices', 'ewww_image_optimizer_tool_installation_failed_notice' );
	}
	ewwwio_memory( __FUNCTION__ );
}

/**
 * Checks which tools should be skipped.
 *
 * @return array {
 *     A list of tools to skip.
 *
 *     @type bool $jpegtran
 *     @type bool $optipng
 *     @type bool $gifsicle
 *     @type bool $pngout
 *     @type bool $pngquant
 *     @type bool $webp
 * }
 */
function ewww_image_optimizer_skip_tools() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	$skip['jpegtran'] = false;
	$skip['optipng']  = false;
	$skip['gifsicle'] = false;
	$skip['pngout']   = false;
	// Except these which are off by default.
	$skip['pngquant'] = true;
	$skip['webp']     = true;
	// If the user has disabled a tool, we aren't going to bother checking to see if it is there.
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 0 || ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) > 10 ) {
		$skip['jpegtran'] = true;
	}
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 0 || ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 ) ) {
		$skip['optipng'] = true;
	}
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) == 0 || ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
		$skip['gifsicle'] = true;
	}
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) || ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 0 || ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 ) ) {
		$skip['pngout'] = true;
	}
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
		$skip['pngquant'] = false;
	}
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) && ! ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) > 10 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) > 10 ) ) {
		$skip['webp'] = false;
	}
	foreach ( $skip as $tool => $disabled ) {
		if ( ! $disabled ) {
			ewwwio_debug_message( "enabled: $tool" );
		}
	}
	return $skip;
}

/**
 * See if the plugin should avoid the exec() function.
 */
function ewww_image_optimizer_define_noexec() {
	if ( defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) ) {
		return;
	}
	// Check if exec is disabled.
	if ( ewww_image_optimizer_exec_check() ) {
		define( 'EWWW_IMAGE_OPTIMIZER_NOEXEC', true );
		ewwwio_debug_message( 'exec seems to be disabled' );
		ewww_image_optimizer_disable_tools();
	} else {
		define( 'EWWW_IMAGE_OPTIMIZER_NOEXEC', false );
	}
}

/**
 * Checks for safe mode and exec, then displays an error if needed.
 *
 * @param string $quiet Optional. Use 'quiet' to suppress warning messages.
 */
function ewww_image_optimizer_notice_utils( $quiet = null ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	// Check if exec is disabled.
	if ( ewww_image_optimizer_exec_check() ) {
		// Need to be a little particular with the quiet parameter.
		if ( 'quiet' !== $quiet ) {
			// Display a warning if exec() is disabled, can't run local tools without it.
			echo "<div id='ewww-image-optimizer-warning-exec' class='error'><p>" . esc_html__( 'EWWW Image Optimizer requires exec() or an API key. Your system administrator has disabled the exec() function, ask them to enable it.', 'ewww-image-optimizer' ) . '</p></div>';
		}
		if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) ) {
			define( 'EWWW_IMAGE_OPTIMIZER_NOEXEC', true );
		}
		ewwwio_debug_message( 'exec seems to be disabled' );
		ewww_image_optimizer_disable_tools();
		return;
	} else {
		if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) ) {
			define( 'EWWW_IMAGE_OPTIMIZER_NOEXEC', false );
		}
	}

	$skip = ewww_image_optimizer_skip_tools();
	// Attempt to retrieve values for utility paths, and store them in the appropriate variables.
	$required = ewww_image_optimizer_path_check( ! $skip['jpegtran'], ! $skip['optipng'], ! $skip['gifsicle'], ! $skip['pngout'], ! $skip['pngquant'], ! $skip['webp'] );
	$missing  = array();
	// Go through each of the required tools.
	foreach ( $required as $key => $req ) {
		// if the tool wasn't found, add it to the $missing array if we are supposed to check the tool in question.
		switch ( $key ) {
			case 'JPEGTRAN':
				if ( ! $skip['jpegtran'] && empty( $req ) ) {
					$missing[] = 'jpegtran';
					$req       = false;
				}
				if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
					ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
					define( 'EWWW_IMAGE_OPTIMIZER_' . $key, $req );
				}
				break;
			case 'OPTIPNG':
				if ( ! $skip['optipng'] && empty( $req ) ) {
					$missing[] = 'optipng';
					$req       = false;
				}
				if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
					ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
					define( 'EWWW_IMAGE_OPTIMIZER_' . $key, $req );
				}
				break;
			case 'GIFSICLE':
				if ( ! $skip['gifsicle'] && empty( $req ) ) {
					$missing[] = 'gifsicle';
					$req       = false;
				}
				if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
					ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
					define( 'EWWW_IMAGE_OPTIMIZER_' . $key, $req );
				}
				break;
			case 'PNGOUT':
				if ( ! $skip['pngout'] && empty( $req ) ) {
					$missing[] = 'pngout';
					$req       = false;
				}
				if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
					ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
					define( 'EWWW_IMAGE_OPTIMIZER_' . $key, $req );
				}
				break;
			case 'PNGQUANT':
				if ( ! $skip['pngquant'] && empty( $req ) ) {
					$missing[] = 'pngquant';
					$req       = false;
				}
				if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
					ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
					define( 'EWWW_IMAGE_OPTIMIZER_' . $key, $req );
				}
				break;
			case 'CWEBP':
				if ( ! $skip['webp'] && empty( $req ) ) {
					$missing[] = 'webp';
					$req       = false;
				}
				if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
					ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
					define( 'EWWW_IMAGE_OPTIMIZER_' . $key, $req );
				}
				break;
		} // End switch().
	} // End foreach().
	// Expand the missing utilities list for use in the error message.
	$msg = implode( ', ', $missing );
	// If there is a message, display the warning.
	if ( ! empty( $msg ) && 'quiet' !== $quiet ) {
		if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
			// Need to include the plugin library for the is_plugin_active function.
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
		}
		if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) {
			$settings_page = 'settings.php?page=' . EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL;
		} else {
			$settings_page = 'options-general.php?page=' . EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL;
		}
		if ( ! is_dir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) {
			ewww_image_optimizer_tool_folder_notice();
		} elseif ( ! is_writable( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) {
			ewww_image_optimizer_tool_folder_permissions_notice();
		}
		echo "<div id='ewww-image-optimizer-warning-opt-missing' class='error'><p>" .
		/* translators: 1-6: jpegtran, optipng, pngout, pngquant, gifsicle, and cwebp (links) 7: Settings Page (link) 8: Installation Instructions (link) */
		sprintf( esc_html__( 'EWWW Image Optimizer uses %1$s, %2$s, %3$s, %4$s, %5$s, and %6$s. You are missing: %7$s. Please install via the %8$s or the %9$s.', 'ewww-image-optimizer' ), "<a href='http://jpegclub.org/jpegtran/'>jpegtran</a>", "<a href='http://optipng.sourceforge.net/'>optipng</a>", "<a href='http://advsys.net/ken/utils.htm'>pngout</a>", "<a href='http://pngquant.org/'>pngquant</a>", "<a href='http://www.lcdf.org/gifsicle/'>gifsicle</a>", "<a href='https://developers.google.com/speed/webp/'>cwebp</a>", $msg, "<a href='$settings_page'>" . esc_html__( 'Settings Page', 'ewww-image-optimizer' ) . '</a>', "<a href='https://docs.ewww.io/'>" . esc_html__( 'Installation Instructions', 'ewww-image-optimizer' ) . '</a>' ) . '</p></div>';
		ewwwio_memory( __FUNCTION__ );
	}
}

/**
 * Checks if exec() is disabled.
 *
 * @return bool True if exec() is disabled.
 */
function ewww_image_optimizer_exec_check() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	if ( ! ewww_image_optimizer_function_exists( 'exec' ) ) {
		return true;
	}
	return false;
}

/**
 * Sends each tool to the binary checker appropriate for the operating system.
 *
 * @param bool $j True to check jpegtran.
 * @param bool $o True to check optipng.
 * @param bool $g True to check gifsicle.
 * @param bool $p True to check pngout.
 * @param bool $q True to check pngquant.
 * @param bool $w True to check cwebp.
 * @return array Path for each tool (indexes JPEGTRAN, OPTIPNG, GIFSICLE, PNGOUT, PNGQUANT, CWEBP),
 *               or false for disabled/missing tools.
 */
function ewww_image_optimizer_path_check( $j = true, $o = true, $g = true, $p = true, $q = true, $w = true ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	$jpegtran = false;
	$optipng  = false;
	$gifsicle = false;
	$pngout   = false;
	$pngquant = false;
	$webp     = false;
	ewww_image_optimizer_define_noexec();
	if ( EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
		return array(
			'JPEGTRAN' => false,
			'OPTIPNG'  => false,
			'GIFSICLE' => false,
			'PNGOUT'   => false,
			'PNGQUANT' => false,
			'CWEBP'    => false,
		);
	}
	if ( 'WINNT' == PHP_OS ) {
		if ( $j ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN' ) ) {
				$jpegtran = ewww_image_optimizer_find_win_binary( 'jpegtran', 'j' );
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_JPEGTRAN' );
				define( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN', $jpegtran );
			} else {
				$jpegtran = EWWW_IMAGE_OPTIMIZER_JPEGTRAN;
			}
		}
		if ( $o ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_OPTIPNG' ) ) {
				$optipng = ewww_image_optimizer_find_win_binary( 'optipng', 'o' );
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_OPTIPNG' );
				define( 'EWWW_IMAGE_OPTIMIZER_OPTIPNG', $optipng );
			} else {
				$optipng = EWWW_IMAGE_OPTIMIZER_OPTIPNG;
			}
		}
		if ( $g ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_GIFSICLE' ) ) {
				$gifsicle = ewww_image_optimizer_find_win_binary( 'gifsicle', 'g' );
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_GIFSICLE' );
				define( 'EWWW_IMAGE_OPTIMIZER_GIFSICLE', $gifsicle );
			} else {
				$gifsicle = EWWW_IMAGE_OPTIMIZER_GIFSICLE;
			}
		}
		if ( $p ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_PNGOUT' ) ) {
				$pngout = ewww_image_optimizer_find_win_binary( 'pngout', 'p' );
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_PNGOUT' );
				define( 'EWWW_IMAGE_OPTIMIZER_PNGOUT', $pngout );
			} else {
				$pngout = EWWW_IMAGE_OPTIMIZER_PNGOUT;
			}
		}
		if ( $q ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_PNGQUANT' ) ) {
				$pngquant = ewww_image_optimizer_find_win_binary( 'pngquant', 'q' );
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_PNGQUANT' );
				define( 'EWWW_IMAGE_OPTIMIZER_PNGQUANT', $pngquant );
			} else {
				$pngquant = EWWW_IMAGE_OPTIMIZER_PNGQUANT;
			}
		}
		if ( $w ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CWEBP' ) ) {
				$webp = ewww_image_optimizer_find_win_binary( 'cwebp', 'w' );
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_CWEBP' );
				define( 'EWWW_IMAGE_OPTIMIZER_CWEBP', $webp );
			} else {
				$webp = EWWW_IMAGE_OPTIMIZER_CWEBP;
			}
		}
	} else {
		if ( $j ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN' ) ) {
				$jpegtran = ewww_image_optimizer_find_nix_binary( 'jpegtran', 'j' );
				if ( ! $jpegtran ) {
					$jpegtran = ewww_image_optimizer_find_nix_binary( 'jpegtran', 'jb' );
				}
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_JPEGTRAN' );
				define( 'EWWW_IMAGE_OPTIMIZER_JPEGTRAN', $jpegtran );
			} else {
				$jpegtran = EWWW_IMAGE_OPTIMIZER_JPEGTRAN;
			}
		}
		if ( $o ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_OPTIPNG' ) ) {
				$optipng = ewww_image_optimizer_find_nix_binary( 'optipng', 'o' );
				if ( ! $optipng ) {
					$optipng = ewww_image_optimizer_find_nix_binary( 'optipng', 'ob' );
				}
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_OPTIPNG' );
				define( 'EWWW_IMAGE_OPTIMIZER_OPTIPNG', $optipng );
			} else {
				$optipng = EWWW_IMAGE_OPTIMIZER_OPTIPNG;
			}
		}
		if ( $g ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_GIFSICLE' ) ) {
				$gifsicle = ewww_image_optimizer_find_nix_binary( 'gifsicle', 'g' );
				if ( ! $gifsicle ) {
					$gifsicle = ewww_image_optimizer_find_nix_binary( 'gifsicle', 'gb' );
				}
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_GIFSICLE' );
				define( 'EWWW_IMAGE_OPTIMIZER_GIFSICLE', $gifsicle );
			} else {
				$gifsicle = EWWW_IMAGE_OPTIMIZER_GIFSICLE;
			}
		}
		if ( $p ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_PNGOUT' ) ) {
				// Pngout is special and has a dynamic and static binary to check.
				$pngout = ewww_image_optimizer_find_nix_binary( 'pngout-static', 'p' );
				if ( ! $pngout ) {
					$pngout = ewww_image_optimizer_find_nix_binary( 'pngout', 'p' );
				}
				if ( ! $pngout ) {
					$pngout = ewww_image_optimizer_find_nix_binary( 'pngout-static', 'pb' );
				}
				if ( ! $pngout ) {
					$pngout = ewww_image_optimizer_find_nix_binary( 'pngout', 'pb' );
				}
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_PNGOUT' );
				define( 'EWWW_IMAGE_OPTIMIZER_PNGOUT', $pngout );
			} else {
				$pngout = EWWW_IMAGE_OPTIMIZER_PNGOUT;
			}
		}
		if ( $q ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_PNGQUANT' ) ) {
				$pngquant = ewww_image_optimizer_find_nix_binary( 'pngquant', 'q' );
				if ( ! $pngquant ) {
					$pngquant = ewww_image_optimizer_find_nix_binary( 'pngquant', 'qb' );
				}
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_PNGQUANT' );
				define( 'EWWW_IMAGE_OPTIMIZER_PNGQUANT', $pngquant );
			} else {
				$pngquant = EWWW_IMAGE_OPTIMIZER_PNGQUANT;
			}
		}
		if ( $w ) {
			if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CWEBP' ) ) {
				$webp = ewww_image_optimizer_find_nix_binary( 'cwebp', 'w' );
				if ( ! $webp ) {
					$webp = ewww_image_optimizer_find_nix_binary( 'cwebp', 'wb' );
				}
				ewwwio_debug_message( 'defining EWWW_IMAGE_OPTIMIZER_CWEBP' );
				define( 'EWWW_IMAGE_OPTIMIZER_CWEBP', $webp );
			} else {
				$webp = EWWW_IMAGE_OPTIMIZER_CWEBP;
			}
		}
	} // End if().
	if ( $jpegtran ) {
		ewwwio_debug_message( "using: $jpegtran" );
	}
	if ( $optipng ) {
		ewwwio_debug_message( "using: $optipng" );
	}
	if ( $gifsicle ) {
		ewwwio_debug_message( "using: $gifsicle" );
	}
	if ( $pngout ) {
		ewwwio_debug_message( "using: $pngout" );
	}
	if ( $pngquant ) {
		ewwwio_debug_message( "using: $pngquant" );
	}
	if ( $webp ) {
		ewwwio_debug_message( "using: $webp" );
	}
	ewwwio_memory( __FUNCTION__ );
	return array(
		'JPEGTRAN' => $jpegtran,
		'OPTIPNG'  => $optipng,
		'GIFSICLE' => $gifsicle,
		'PNGOUT'   => $pngout,
		'PNGQUANT' => $pngquant,
		'CWEBP'    => $webp,
	);
}

/**
 * Checks the binary against a list of valid sha256 checksums (formerly md5sums).
 *
 * @param string $path The filename of a binary to check for a match.
 * @return bool True if the sha256sum is validated.
 */
function ewww_image_optimizer_md5check( $path ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	$binary_sum = hash_file( 'sha256', $path );
	ewwwio_debug_message( "$path: $binary_sum" );
	$valid_sums = array(
		'463de9ba684d54d27185cb6487a0b22b7571a87419abde4dee72c9b107f23315', // jpegtran-mac 9, EWWW 1.3.0.
		'0b94f82e3d740d1853281e9aaee5cc7122c27fd63da9d6d62ed3398997cbed1e', // jpegtran-linux 9, EWWW 1.4.0.
		'f5f079bfe6f3f48c17738679292f35cdee44afe8f8413cdbc4f555cee7de4173', // jpegtran-linux64 9, EWWW 1.4.0.
		'ec71f638d2101f08fab66f4d139746d4042352bc75d55bd093aa446081892e0c', // jpegtran-fbsd 9, EWWW 1.4.0.
		'356532227fce51fcb9df29f143ab9d202fbd40f18e2b8234aee95937c93bd67e', // jpegtran-fbsd64 9, EWWW 1.4.0.
		'7be857837764dff4f0d7d2c5d546bf4d2573af7f326ced908ac229d60fd054c6', // jpegtran.exe 9, EWWW 1.4.0.
		'bce5205bb240532c01273b5442a44244a8a27a74fb47e2ce467c18b91fabea6b', // jpegtran-sol 9, EWWW 1.7.4.
		'cadc7be4688632bf2860562a1596f1b2b54b9a9c8b27df7ecabca49b1dcd8a5f', // jpegtran-fbsd 9a, EWWW 2.0.0.
		'bab4aa853c143534503464eeb35893d16799cf859ff22f9a4e62aa383f4fc99c', // jpegtran-fbsd64 9a, EWWW 2.0.0.
		'deb7e0f579fac767196611aa110052864e3093017970ff74de709b41e265e8b1', // jpegtran-linux 9a, EWWW 2.0.0.
		'b991fde396ebcc0e4f805df44b1797fe369f7f19e9392684dd4052e3f23c441e', // jpegtran-linux64 9a, EWWW 2.0.0.
		'436835bd42b27d2f05440bc5dc5174f2a896d38f8a550d96704d39969951d9ac', // jpegtran-mac 9a, EWWW 2.0.0.
		'bdf3c6b6cb16287a3f62e7cde8f69f8bda5d310abca28e00068c526f9f37cc89', // jpegtran-sol 9a, EWWW 2.0.0.
		'3c2746d0b1ae150b13b767715af45ff601e394c01ada929cbe16e6dcd18fb199', // jpegtran.exe 9a, EWWW 2.0.0.
		'8e11f7df5735b36d3ecc95c84b0e355355a766d3ccafbf751bcf343a8952432c', // jpegtran-fbsd 9b, EWWW 2.6.0.
		'21d8046e07cb298dfd2f3b1e321c67c378a4d35fa8adc3521acc42b5b8088d64', // jpegtran-linux 9b, EWWW 2.6.0.
		'4d1a1c601d291f96dc03ea7e42ab9137a17f93ebc391353db65b4e32c1e9fbdb', // jpegtran-mac 9b, EWWW 2.6.0.
		'7e8719703d31e1ab9bf2b2ad7ab633649012ab6aae46ea40462365b9c00876d5', // jpegtran-sol 9b, EWWW 2.6.0.
		'9767f05ae1b59d4fea25a73b276dcd1245f5281b53386dc03784539265bffbea', // jpegtran.exe 9b, EWWW 2.6.0.
		// end jpegtran.
		'6deddb5562ac13ffc3e46a0af79b592e92fb4553c5df294b6e0052bc890fd0e3', // optipng-linux 0.7.4, EWWW 1.2.0.
		'51df81fa8c765efbe0aa4c1cf5293e25e7e2e7f6962f5161615239c54aec4c01', // optipng-linux 0.7.4, EWWW 1.3.0.
		'7a56cca66471ce2b6cdff4460db0d75258ef05de8da1eda0448e4d4ad9ae252f', // optipng-mac 0.7.4, EWWW 1.3.0.
		'2f9140cdc3ef1f7687baa710f0bba84c5f7f11e3f62c3ce43124e23b849ac5ff', // optipng-linux 0.7.4, EWWW 1.3.7.
		'5d59467363c457bf743f4df121c365dd43365357f1cdea5f3752a7ca1b3e315a', // optipng-fbsd 0.7.4, EWWW 1.4.0.
		'1af8077958a88a3064a71903841f901179e27fe137774085565619fb199c653a', // optipng.exe 0.7.4, EWWW 1.4.0.
		'f692fef395b8689de033b9f2ce80c867c8a229c52e948df733377e20b62773a9', // optipng-sol 0.7.4, EWWW 1.7.4.
		'e17d327cd89ab34eff7f994806fe9f2c124d6cc6cd309fa4c3911d5ce90312c9', // optipng-fbsd 0.7.5, EWWW 2.0.0.
		'd263ecfb5b29ed08920e26cf604a86d3484daee5b80605e445cf97aa14d8aebc', // optipng-linux 0.7.5, EWWW 2.0.0.
		'6f15cb2e8d25e51037efa7bcec7499c96eb11e576536a478edfee500207655ae', // optipng-mac 0.7.5, EWWW 2.0.0.
		'1d2de40b009f16e9c709f9b0c15a47abb8da57668a918ac9a0723ddc6de6c30a', // optipng-sol 0.7.5, EWWW 2.0.0.
		'fad3a0fd95706d53576f72593bf13d3e31d1c896c852bfd5b9ba602eca0bd2b6', // optipng.exe 0.7.5, EWWW 2.0.0.
		'9d60eaeb9dc5167a57a5f3af236d56b4149d1043b543f2faa38a0936fa6b54b2', // optipng-fbsd 0.7.6, EWWW 2.8.0.
		'853ca5936a2dd92a17b3518fd55db6be35e1b2bebfabca3949c34700072e08b8', // optipng-linux 0.7.6, EWWW 2.8.0.
		'd4f11e96733aed64a72e744843dcd0929e144a7fc97f40d405a034a72eb9bbc6', // optipng-mac 0.7.6, EWWW 2.8.0.
		'1ed9343194cfca0a1c32677c974192746adfd48cb4cea6a2df668452df0e68f7', // optipng-sol 0.7.6, EWWW 2.8.0.
		'03b86ce2c08e2cc78d76d3d3dd173986b498b055c3c19e13a97a7c3c674772c6', // optipng.exe 0.7.6, EWWW 2.8.0.
		'f01cba0ab658e08738315843ee635be273726bf102ae448416b3d8956843d864', // optipng-fbsd 0.7.7 EWWW 4.1.0.
		'4404076a4f9119d4dfbb7acb00eb65345e804186a019c7136d8f8e87fb0cb997', // optipng-linux 0.7.7 EWWW 4.1.0.
		'36535c1b262e0c457bbb0ed2bc71e812a49e26a6cada63b6acbd8d809c68a5a1', // optipng-mac 0.7.7 EWWW 4.1.0.
		'41a4c78e6c97ea26836f4b021157b34f1812a9e5c2341502aad8cde942b18576', // optipng-sol 0.7.7 EWWW 4.1.0.
		'6a321e07eca8e28fa8a969b5db3c1d3cc008a2064d636cf74762bbe4364b7b14', // optipng.exe 0.7.7 EWWW 4.1.0.
		// end optipng.
		'a2292c0085863a65c99cb41ff8418ce63033e162906df72e8fdde52f0633579b', // gifsicle linux 1.67, EWWW 1.2.0.
		'd7f9609b6fd0000b2eaad2bd0c3cb85476988b18705762e915bda3f2e6007801', // gifsicle-linux 1.68, EWWW 1.3.0.
		'204a839a50367adb8cd23fae5d1913a5ca8b41307f054156ed152748d3e7934d', // gifsicle-linux 1.68, EWWW 1.3.7.
		'23e208099fa7ce75a3f98144190d6362d69b90c6f0a534ffa45dbbf789f7d99c', // gifsicle-mac 1.68, EWWW 1.3.0.
		'8b08243a7cc655512a03403f6c3814176e28bbd140df7c059bd321a9a0151c18', // gifsicle-fbsd 1.70, EWWW 1.4.0.
		'fd074673967ee9d387208f047c081a6331663b4076f4a6a608d6f646622af718', // gifsicle-linux 1.70, EWWW 1.4.0 - 1.7.4.
		'bc32a390e86d2d8f40e970b2dc059015b51afe26794d92a936c1fe7216db805d', // gifsicle-mac 1.70, EWWW 1.4.0.
		'41e67a35cd178f781b5224d196185e4243e6c2b3bece43277130fe07cdda402f', // gifsicle-sol 1.70, EWWW 1.7.4.
		'3c6d9fabd1ea1014b8f58063dd00a653980c06bc1b45e96a47d866247263a1e1', // gifsicle.exe 1.70, EWWW 1.4.0.
		'decba7a95b637bee53847af680fd37bde8bd568528412c514b7bd794056fd4ff', // gifsicle-fbsd 1.78, EWWW 1.7.5.
		'c28e5e4b5344f77f415973d013e4cb393fc550e8de44117b090d534e98b30d1c', // gifsicle-linux 1.78, EWWW 1.7.5 - 1.9.3.
		'fc2de863e8579b0d540003300e918cee450bc8e026018c631dffc0ed851a8c1c', // gifsicle-mac 1.78, EWWW 1.7.5.
		'74d011ee1b6d9fe6d5d8bdb4cd17db0c5987fa6e3d495b42439cd70b0763c07a', // gifsicle-sol 1.78, EWWW 1.7.5.
		'7c10da38f4afb28373779d40a30710aa9fb369e82f7f29363554bea965d132df', // gifsicle.exe 1.78, EWWW 1.7.5.
		'e75acedd0725fba64ee72855b796cdfa8dac9959d63e89a9e0e5ba059ae013c2', // gifsicle-fbsd 1.84, EWWW 2.0.0.
		'a4f0f21bc4bea51f5d304fe944262c12f671d70a3e5f688061da7bb036e84ff8', // gifsicle-linux 1.84, EWWW 2.0.0 - 2.4.3.
		'5f4176b3fe69f975563d2ce7e76615ab558f5f1839b9bfa6f6de1b3c3fa11c02', // gifsicle-mac 1.84, EWWW 2.0.0.
		'9f0027bed22d4be60012488ab726c3a131d9f3e1e276e9400c578173347a9a48', // gifsicle-sol 1.84, EWWW 2.0.0.
		'72f0077e8591292d09efee09a181458b34fb3c0e9a6ac7e8e11cec574bf619ac', // gifsicle.exe 1.84, EWWW 2.0.0.
		'c64936b429e46b6a75339df00eb8daa39d335844c906fa16d4d0af481851e91e', // gifsicle-fbsd 1.87, EWWW 2.4.4.
		'deea065a91c8429edecf42ccef78636065f7ae0dad867df7696128c6711e4735', // gifsicle-linux 1.87, EWWW 2.4.4.
		'2e0d8b7413173555bbec6e019c3cd7c55f7d582a017a0af7b14cfd24a6921f51', // gifsicle-mac 1.87, EWWW 2.4.4.
		'3966e01474601059c6a13aefbe4f313c6cb6d49c799f7850966950892a9ab45a', // gifsicle-sol 1.87, EWWW 2.4.4.
		'40b86b2ea6642f4c921152923af1e631922b624f7d23189f53c659506c7179b5', // gifsicle.exe 1.87, EWWW 2.4.4.
		'3da9e1a764a459d78dc1468ba60d882ff042050a86f82d895777b172b50f2f19', // gifsicle.exe 1.87, EWWW 2.4.5.
		'327c21635ea8c789e3e9533210e6baf372db27c7bbed3791881d74a7dd41cef9', // gifsicle-fbsd 1.91, EWWW 4.1.0.
		'566f058b2043c4f3c8c049b0507bfa78dcb33dac52b132cade5f67bbb62d91e4', // gifsicle-linux 1.91, EWWW 4.1.0.
		'03602b141432af2211882fc079ba15a773a7ec782c92755cb31279eb6d8b99d4', // gifsicle-mac 1.91, EWWW 4.1.0.
		'5fcdd102146984e41b01a160d072dd36852d7be14ab569a323c47e7e56916d0d', // gifsicle-sol 1.91, EWWW 4.x.
		'7156bfe16dc5e33af7facdc6847d268154ffeb75c0217517e4e188b58b293c6a', // gifsicle.exe 1.91, EWWW 4.1.0.
		// end gifsicle.
		'bdea95497d6e60aae8938cae8e999ef74a255ad603531bf523dcdb531f61fc8f', // 20110722-bsd/i686/pngout.
		'57c09b3ebd7d4623d16f6056efd7951e8f98e2362a27993a7d865af677875c00', // 20110722-bsd-static/i686/pngout-static.
		'17960599ca28a61aeb883a68b2eb52c513b730a410a0db75a7c2c22e0a3f925a', // 20110722-linux/i686/pngout.
		'689f68bcbf39e68cdf0f0a350d59c0acafdbcf7ff122e25b5a8b58ed3a8f18ef', // 20110722-linux/x86_64/pngout.
		'2028eea62f04b074b7693e5ce625c848ff6521206782616c893ca93637644a51', // 20110722-linux-static/i686/pngout-static.
		'7d071c3a6ac9c4e8077f029dbba1cde49008d38adf897401e951f9c2e7ce8bb1', // 20110722-linux-static/x86_64/pngout-static.
		'89c510b551718d263433bb37e67364cab582a71bf7f5558213a121bb86cb5f98', // 20110722-mac/pngout.
		'e383a5293e3b1934c87367799f6eaefbd6714cfa004262f273fb7f2f4d15930b', // 20130221-bsd/i686/pngout.
		'd2b70c882be527543818d84552cc4e6faf40da3cec45286e5c36ed73e9611b7b', // 20130221-bsd/amd64/pngout.
		'bc08e1f883ba92a04e44fe4e756e1afc3b77fc1d072519adff6ce2f7787109bb', // 20130221-bsd-static/i686/pngout-static.
		'860779de32c1fe34f211da036471d6e4ecc0d35527727d476f29623785cf6f82', // 20130221-bsd-static/amd64/pngout-static.
		'edd8e6173bf3b862c6c40c4b5aad6514169a58ee9b0b34d8c37e475005889592', // 20130221-linux/i686/pngout.
		'f6a053d1c03b69e2ac4435aaa5b5e13ea5169d9a262286595f9f455d8da5caf1', // 20130221-linux/x86_64/pngout.
		'9591669b3984a19f0aab3a8e8fad98c5274b3c30daecf46b35d22df934546618', // 20130221-linux-static/i686/pngout-static.
		'25d2aab99796c26f1e9cf1f2a9713920be40ce1b99e02c2c50b67fa6e3da06be', // 20130221-linux-static/x86_64/pngout-static.
		'57fd225f3ae921309ee4570f1970629d31cb02946983405d1b1f648aeaab30a1', // 20130221-mac/pngout.
		'3dfeb927e96853d2470350b0a916dc263cd4ebe878b402773dba105a6644e796', // 20150319-bsd/i686/pngout.
		'60a2848c79551a3e79ffcea7f54964767e25bb05c2255b0ea6a1eb03605661d2', // 20150319-bsd/amd64/pngout.
		'52dd45f15221f2ff30739151f30aedb5e3377dd6bccd350d4bce9429d7fa5e8b', // 20150319-bsd-static/i686/pngout-static.
		'12ffa454936e1d35dc96749208d740695fea26d07267b6a17b9890db0f156026', // 20150319-bsd-static/amd64/pngout-static.
		'5b97595c2b4e5f47ba797b105b3b56dbb769437bdc9092f07f6c57bc457ee667', // 20150319-linux/i686/pngout.
		'a496985d02c785c05f21f653fc4d61a5a171a68f691119448bc3c3152246f0d7', // 20150319-linux/x86_64/pngout.
		'b6641cb01b684c42e40076b91f98485dd115f6200d3f0baf989f1a4ae43c603a', // 20150319-linux-static/i686/pngout-static.
		'2b8245fe21a648101b8e7399a9dfcc4cf42a39dafa7aab673a7c47901bf82e4a', // 20150319-linux-static/x86_64/pngout-static.
		'12afd90e04387d4c3be985042c1eada89e0c4504f84c0b4739c459c7b3831774', // 20150319-mac/pngout.
		'843f0be42e86680c1663c4ef58eb0677ace15fc29ab23897c83f4b7e5af3ef36', // 20150319-windows/pngout.exe 20150319.
		'aa3993937455094c0f66ac77d60bf53be441fdf8f14618520c2af68f2253085d', // 20150920-mac/pngout.
		// end pngout.
		'8417d5d60bc66442ecc666e31ec7b9e1b7c55f48291e74b4b81f35703e2aef2e', // pngquant-fbsd 2.0.2, EWWW 1.8.3.
		'78668c38d0be70764b18f3f4e0ea2b647df2ae87cedb2216d0ef69c8c55b688a', // pngquant-linux 2.0.2, EWWW 1.8.3.
		'7df1b7f6ed73a189083dd931fb3380d236d34790318f00233b59c8f26f90665f', // pngquant-mac 2.0.2, EWWW 1.8.3.
		'56d2c6212eb595f5eab8a7469e56fa8d3d0e6ffc231aef27742134fba4a39298', // pngquant-sol 2.0.2, EWWW 1.8.3.
		'd3851c962cd59d74a35174bf3ce71d876dfcd8bdf76f81cd428b2ab7e53c0515', // pngquant.exe 2.0.2, EWWW 1.8.3.
		'0ee6f1dbf4fa168b11ce60860e5700ca0e5125323a43540a78c76644835abc84', // pngquant-fbsd 2.3.0, EWWW 2.0.0.
		'85d8a70930a554f50181a1d061577cf67ef2e76e2cbd5bcb1b7f006064ff1444', // pngquant-linux 2.3.0, EWWW 2.0.0.
		'a807f769922fdad0ba07307c548df8cf8eeced649d04237d13dfc95757161459', // pngquant-mac 2.3.0, EWWW 2.0.0.
		'cf2cc40274c438b35e93bd0346c2a6d871bd7a7bdd90c52f4e79f369cb8ded74', // pngquant-sol 2.3.0, EWWW 2.0.0.
		'7454aba77b1a2b63a42d8a5870d3c2d733c7efb2d828643d5e64784af1f65f2a', // pngquant.exe 2.3.0, EWWW 2.0.0.
		'6287f1bb7179c7b6d71a41112222347ed97b6eae4e79b180d7e1e332a4bde3e2', // pngquant-fbsd 2.5.2, EWWW 2.5.4.
		'fcfe4d3a602e7b491f4126a2707144f5f9cc9359d13f443575d7ea6a74e85ddb', // pngquant-linux 2.5.2, EWWW 2.5.4.
		'35794819a35e949dc0c0d6f90d0bb675791fa9bc3f405eb19f48ea31bb6456a8', // pngquant-mac 2.5.2, EWWW 2.5.4.
		'c242586c70d83af544334f1846b838ef68c6ab4fc247b2cff9ad4b714f825866', // pngquant-sol 2.5.2, EWWW 2.5.4.
		'ad79d9b3395d41404b28362972bd68db3c58d5be5f063884df3a595fc38c6a98', // pngquant.exe 2.5.2, EWWW 2.5.4.
		'54d632fc4446d88ad4d1beeaf73420d68d87786f02adc9d3363766cb93ec95a4', // pngquant-fbsd 2.9.1, EWWW 3.4.0.
		'91f704f02468f86766007e46973a1ef9e282d6ccadc54caf339dc537c9b2b61d', // pngquant-linux 2.9.1, EWWW 3.4.0.
		'65dc20f05af588d948fc6f4df37c294f4a3a1c1ad207a8b56d13e6829773165a', // pngquant-mac 2.9.1, EWWW 3.4.0.
		'dbc9e12d5bb3e806aaf5e2c3d30d122d569069027a633485761cbf072cf2236d', // pngquant-sol 2.9.1, EWWW 3.4.0.
		'84e63e6f9f9630a1a0c3e782609349c12b8df9ea9d02c5a29230819379e56b3c', // pngquant.exe 2.8.1, EWWW 3.4.0.
		'd6433dc6ecf6a0fdedf754782e6d5c9e494ddec762426a6d0b1896a220bd6d3f', // pngquant-fbsd 2.11.7 EWWW 4.1.0.
		'40b0860abba39342fb64612a612e0f24571d259b6b83d7483af9a1d586950d79', // pngquant-linux 2.11.7 EWWW 4.1.0.
		'c924e11d9a3166afd5ed19165193c1351ff4a2cc993498f1f28c7daee829ca76', // pngquant-mac 2.11.7 EWWW 4.1.0.
		'34534e69929e7fe267f77c55f487e419f76cc1d24e41fdb642f9671383012c56', // pngquant-sol 2.11.7 EWWW 4.1.0.
		'af7598aa09ba519ad15305a56011949db19c5b2176187662640bc0ebc4ddd19a', // pngquant.exe 2.11.7 EWWW 4.1.0.
		// end pngquant.
		'bf0e12f996802dc114a864e5150647ce41089a5a2b5e36c3a270ac848b655c26', // cwebp-fbsd 0.4.1, EWWW 2.0.0.
		'5349646072c3ef5f8b4588bbee8635e882c245439e2d86b863f04b7e27f4fafe', // cwebp-fbsd64 0.4.1, EWWW 2.0.0.
		'3044c02cfef53f4361f7b2db49c5679f894ed346f665d4c8d91c6675d84dbf67', // cwebp-linux6 0.4.1, EWWW 2.0.0.
		'c9899718a5e272a082fd7c9d93d7c23d8a50f49d1b739a9aa1ef404f78cd7baf', // cwebp-linux664 0.4.1, EWWW 2.0.0.
		'2a0dff5c80fd5fa170babd0c0571f4499606f8d09bf820938da41a311d6dec6f', // cwebp-linux8 0.4.1, EWWW 2.0.0.
		'c1dfbbad935e31bde2e517dff43911c0651a8e5f78c022a252a864278065ae11', // cwebp-linux864 0.4.1, EWWW 2.0.0.
		'bae23f1614d391b136e8618a21590e4a9f0614c8716b86a6a7067527e9950d87', // cwebp-mac7 0.4.1, EWWW 2.0.0.
		'bc72654fead42c6d4fd841cecdee6ccbf21b2407292593ec982f31d39b566955', // cwebp-mac8 0.4.1, EWWW 2.0.0.
		'7fa005dc6a18563e4f6574bec83c92cabf786d8ee845503d80fa52e370dc4903', // cwebp-sol 0.4.1, EWWW 2.0.0.
		'6486779c8e1e9cc7c63ae03c416fc6d5dc7598c58a6cddbe9a41e70d804410f9', // cwebp.exe 0.4.1, EWWW 2.0.0.
		'814a168f190c4712df231b1f7d1910185ef823953b54c9fb8b354f415172a371', // cwebp-fbsd64 0.4.3, EWWW 2.4.4.
		'0f867ea2db0db895612bd15916ad31bc71c89ef2ad74552b7e878df09b843da5', // cwebp-linux6 0.4.3, EWWW 2.4.4.
		'179c7b9a2fbc1af542b3653bff58ca4dcb35bebf346687c12bb667ab49e9e21b', // cwebp-linux664 0.4.3, EWWW 2.4.4.
		'212e59654bbb6147ee8a554bf8eb7b5c11f75b9ef14ac3e6ee92ad726a47339a', // cwebp-linux8 0.4.3, EWWW 2.4.4.
		'b491509221f7c97e8dcc3bdd6f7fc201f40bc93062618bfba06f84aac7704558', // cwebp-linux864 0.4.3, EWWW 2.4.4.
		'2e8c5f53f44656ec80f11cca3c985200f502c88ea47bb34063e09eb6313e04a6', // cwebp-mac8 0.4.2, EWWW 2.4.4.
		'963a09a2c45ba036291b32ecb665541e40c232bb0f2474810ac2a9ddf8837fe4', // cwebp-mac9 0.4.3, EWWW 2.4.4.
		'2642d98bb75bc2fd2d969ba1d27b8628fd7fa73a7a204ed8f71a65e124abcac0', // cwebp-sol 0.4.3, EWWW 2.4.4.
		'64cd62e33201b0d14ec4823b64d93f92825f2e8f5239726f5b00ed9ff944a581', // cwebp.exe 0.4.3, EWWW 2.4.4.
		'7d7329671d445924dafcaacee7f2db6f4ce33567ffca41aa5b5818ebff806bc5', // cwebp-fbsd64 0.4.4, EWWW 2.5.4.
		'f1a48031d0ab602080f5646695ce8a3e84d5470f1be99d1b8fc20aded9c7839b', // cwebp-linux6 0.4.4, EWWW 2.5.4.
		'b2bef90b62d80b35d4c5a41f793454e95e5159bf0aec2e4bd8c19fc3de3556bd', // cwebp-linux664 0.4.4, EWWW 2.5.4.
		'd3c358524efd50f6e078362733870229ca1e1db8885580b6814c2535b4d20612', // cwebp-linux8 0.4.4, EWWW 2.5.4.
		'271deeec579c252e364495addad03d9c1f3248c2177a01638002b25eee787ded', // cwebp-linux864 0.4.4, EWWW 2.5.4.
		'379e2b95e20dd33f4667c134099df358e178f6a6bf32f3a5b6b78bbb6950b4b5', // cwebp-mac9 0.4.4, EWWW 2.5.4.
		'118ea3f0bcdcce6128d64e34159c93c3324cb038c9e5a51efaf530ea52af7070', // cwebp-sol 0.4.4, EWWW 2.5.4.
		'43941c1d7169e66fb1fd62a1950286b230d3e5bec3bbb14fdb4ac091ca7a0f9f', // cwebp.exe 0.4.4, EWWW 2.5.4.
		'26d5d88dee2993d1d0e16f5e60318cd8adec485614facd6c7f9c22c71eb7b2e5', // cwebp-fbsd 0.5.0 EWWW 2.6.0.
		'60b1738d6502691227a46658cd7656b4a52702680f169e8e04d72077e967aeed', // cwebp-linux 0.5.0, EWWW 2.6.0.
		'276a0221a4c978825903572c2b68b3010399375d6b9dc7429286caf625cae95a', // cwebp-mac9 0.5.0, EWWW 2.6.0.
		'be3e81ec7267e7878ddd4ee01df1553966952f74bbfd30a5523d12d53f019ecb', // cwebp-sol 0.5.0, EWWW 2.6.0.
		'b41123ec06f21765f50ec1b017839f99ab4f28497d87da722817a6023e4a3b32', // cwebp.exe 0.5.0, EWWW 2.6.0.
		'f0547a6219c5c05d0af29c5e411e054b9d795567f4ae2e27893815af9383c60f', // cwebp-fbsd 0.5.1, EWWW 2.9.9.
		'9eaf670bb2d567421c7e2918112dc00406c60f008b120f648cf0bdba73ee9b6b', // cwebp-linux 0.5.1, EWWW 2.9.9.
		'1202ea932b315913d3736460dd3d50bc5b251b7a0a8f0468c63144ba427679c2', // cwebp-mac9 0.5.1, EWWW 2.9.9.
		'27ba0abce52e74744f6235fcde9b153b5052b9c15cd78e74feffaea9dafcc178', // cwebp-sol 0.5.1, EWWW 2.9.9.
		'b02864989f0a1a263caa796c5b8caf18c1f774ed0ba08a9350e8820459875f51', // cwebp.exe 0.5.1, EWWW 2.9.9.
		'e5cbea11c97fadffe221fdf57c093c19af2737e4bbd2cb3cd5e908de64286573', // cwebp-fbsd 0.6.0, EWWW 3.4.0.
		'43ca351e8f5d457b898c587151ebe3d8f6cce8dcfb7de44f6cb70148a31a68bc', // cwebp-linux 0.6.0, EWWW 3.4.0.
		'a06a3ee436e375c89dbc1b0b2e8bd7729a55139ae072ed3f7bd2e07de0ebb379', // cwebp-mac12 0.6.0, EWWW 3.4.0.
		'1febaffbb18e52dc2c524cda9eefd00c6db95bc388732868999c0f48deb73b4f', // cwebp-sol 0.6.0, EWWW 3.4.0.
		'49e9cb98db30bfa27936933e6fd94d407e0386802cb192800d9fd824f6476873', // cwebp.exe 0.6.0, EWWW 3.4.0.
		// end cwebp.
	);
	foreach ( $valid_sums as $checksum ) {
		if ( $checksum === $binary_sum ) {
			ewwwio_debug_message( 'checksum verified, binary is intact' );
			ewwwio_memory( __FUNCTION__ );
			return true;
		}
	}
	ewwwio_memory( __FUNCTION__ );
	return false;
}

/**
 * Check the mimetype of the given file with various methods.
 *
 * Checks WebP files using a direct pattern match, then prefers fileinfo, getimagesize (for images
 * only), mime_content_type, and lastly the 'file' utility (only for binaries).
 *
 * @param string $path The absolute path to the file.
 * @param string $case The type of file we are checking. Accepts 'i' for
 *                     images/pdfs or 'b' for binary.
 * @return bool|string A valid mime-type or false.
 */
function ewww_image_optimizer_mimetype( $path, $case ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	ewwwio_debug_message( "testing mimetype: $path" );
	$type = false;
	// For S3 images/files, don't attempt to read the file, just use the quick (filename) mime check.
	if ( 'i' === $case && strpos( $path, 's3' ) === 0 ) {
		return ewww_image_optimizer_quick_mimetype( $path );
	}
	if ( 'i' === $case && preg_match( '/^RIFF.+WEBPVP8/', file_get_contents( $path, null, null, 0, 16 ) ) ) {
		/* return 'image/webp'; */
	}
	if ( 'i' === $case ) {
		$fhandle = fopen( $path, 'rb' );
		if ( $fhandle ) {
			// Read first 12 bytes, which equates to 24 hex characters.
			$magic = bin2hex( fread( $fhandle, 12 ) );
			if ( 0 === strpos( $magic, '52494646' ) && 16 === strpos( $magic, '57454250' ) ) {
				$type = 'image/webp';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			if ( 'ffd8ff' === substr( $magic, 0, 6 ) ) {
				$type = 'image/jpeg';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			if ( '89504e470d0a1a0a' === substr( $magic, 0, 16 ) ) {
				$type = 'image/png';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			if ( '474946383761' === substr( $magic, 0, 12 ) || '474946383961' === substr( $magic, 0, 12 ) ) {
				$type = 'image/gif';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			if ( '25504446' === substr( $magic, 0, 8 ) ) {
				$type = 'application/pdf';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			ewwwio_debug_message( "match not found for image: $magic" );
		} else {
			ewwwio_debug_message( 'could not open for reading' );
		}
	}
	if ( 'b' === $case ) {
		$fhandle = fopen( $path, 'rb' );
		if ( $fhandle ) {
			// Read first 4 bytes, which equates to 8 hex characters.
			$magic = bin2hex( fread( $fhandle, 4 ) );
			// Mac (Mach-O) binary.
			if ( 'cffaedfe' === $magic || 'feedface' === $magic || 'feedfacf' === $magic || 'cefaedfe' === $magic || 'cafebabe' === $magic ) {
				$type = 'application/x-executable';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			// ELF (Linux or BSD) binary.
			if ( '7f454c46' === $magic ) {
				$type = 'application/x-executable';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			// MS (DOS) binary.
			if ( '4d5a9000' === $magic ) {
				$type = 'application/x-executable';
				ewwwio_debug_message( "ewwwio type: $type" );
				return $type;
			}
			ewwwio_debug_message( "match not found for binary: $magic" );
		} else {
			ewwwio_debug_message( 'could not open for reading' );
		}
	}
	return false;
}

/**
 * Escape any spaces in the filename.
 *
 * @param string $path The path to a binary file.
 * @return string The path with spaces escaped.
 */
function ewww_image_optimizer_escapeshellcmd( $path ) {
	return ( preg_replace( '/ /', '\ ', $path ) );
}

/**
 * Test the given binary to see if it returns a valid version string.
 *
 * @param string $path The absolute path to a binary file.
 * @param string $tool The specific tool to test. Accepts j, o, g, p, q, w or append a b for blind
 *                     test like jb.
 * @return bool True (or truthy) if found.
 */
function ewww_image_optimizer_tool_found( $path, $tool ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	ewwwio_debug_message( "testing case: $tool at $path" );
	// '*b' cases are 'blind' testing in case we can't get at the version string, but the binaries are actually working, we run a test compression, and compare the results with what they should be.
	switch ( $tool ) {
		case 'j': // jpegtran.
			exec( $path . ' -v ' . EWWW_IMAGE_OPTIMIZER_IMAGES_PATH . 'sample.jpg 2>&1', $jpegtran_version );
			if ( ewww_image_optimizer_iterable( $jpegtran_version ) ) {
				ewwwio_debug_message( "$path: {$jpegtran_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			foreach ( $jpegtran_version as $jout ) {
				if ( preg_match( '/Independent JPEG Group/', $jout ) ) {
					ewwwio_debug_message( 'optimizer found' );
					return $jout;
				}
			}
			break;
		case 'jb':
			$upload_dir = wp_upload_dir();
			$testjpg    = trailingslashit( $upload_dir['basedir'] ) . 'testopti.jpg';
			exec( $path . ' -copy none -optimize -outfile ' . ewww_image_optimizer_escapeshellarg( $testjpg ) . ' ' . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_IMAGES_PATH . 'testorig.jpg' ) );
			$testjpgsize = ewww_image_optimizer_filesize( $testjpg );
			ewwwio_debug_message( "blind testing jpegtran, is $testjpgsize smaller than 5700?" );
			if ( $testjpgsize ) {
				unlink( $testjpg );
			}
			if ( 0 < $testjpgsize && $testjpgsize < 5700 ) {
				ewwwio_debug_message( 'optimizer found' );
				return esc_html__( 'unknown', 'ewww-image-optimizer' );
			}
			break;
		case 'o': // optipng.
			exec( $path . ' -v 2>&1', $optipng_version );
			if ( ewww_image_optimizer_iterable( $optipng_version ) ) {
				ewwwio_debug_message( "$path: {$optipng_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $optipng_version ) && strpos( $optipng_version[0], 'OptiPNG' ) === 0 ) {
				ewwwio_debug_message( 'optimizer found' );
				return $optipng_version[0];
			}
			break;
		case 'ob':
			$upload_dir = wp_upload_dir();
			$testpng    = trailingslashit( $upload_dir['basedir'] ) . 'testopti.png';
			exec( $path . ' -out ' . ewww_image_optimizer_escapeshellarg( $testpng ) . ' -o1 -quiet -strip all ' . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_IMAGES_PATH . 'testorig.png' ) );
			$testpngsize = ewww_image_optimizer_filesize( $testpng );
			ewwwio_debug_message( "blind testing optipng, is $testpngsize smaller than 110?" );
			if ( $testpngsize ) {
				unlink( $testpng );
			}
			if ( 0 < $testpngsize && $testpngsize < 110 ) {
				ewwwio_debug_message( 'optimizer found' );
				return esc_html__( 'unknown', 'ewww-image-optimizer' );
			}
			break;
		case 'g': // gifsicle.
			exec( $path . ' --version 2>&1', $gifsicle_version );
			if ( ewww_image_optimizer_iterable( $gifsicle_version ) ) {
				ewwwio_debug_message( "$path: {$gifsicle_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $gifsicle_version ) && strpos( $gifsicle_version[0], 'LCDF Gifsicle' ) === 0 ) {
				ewwwio_debug_message( 'optimizer found' );
				return $gifsicle_version[0];
			}
			break;
		case 'gb':
			$upload_dir = wp_upload_dir();
			$testgif    = trailingslashit( $upload_dir['basedir'] ) . 'testopti.gif';
			exec( $path . ' -O3 -o ' . ewww_image_optimizer_escapeshellarg( $testgif ) . ' ' . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_IMAGES_PATH . 'testorig.gif' ) );
			$testgifsize = ewww_image_optimizer_filesize( $testgif );
			ewwwio_debug_message( "blind testing gifsicle, is $testgifsize smaller than 12000?" );
			if ( $testgifsize ) {
				unlink( $testgif );
			}
			if ( 0 < $testgifsize && $testgifsize < 12000 ) {
				ewwwio_debug_message( 'optimizer found' );
				return esc_html__( 'unknown', 'ewww-image-optimizer' );
			}
			break;
		case 'p': // pngout.
			exec( "$path 2>&1", $pngout_version );
			if ( ewww_image_optimizer_iterable( $pngout_version ) ) {
				ewwwio_debug_message( "$path: {$pngout_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $pngout_version ) && strpos( $pngout_version[0], 'PNGOUT' ) === 0 ) {
				ewwwio_debug_message( 'optimizer found' );
				return $pngout_version[0];
			}
			break;
		case 'pb':
			$upload_dir = wp_upload_dir();
			$testpng    = trailingslashit( $upload_dir['basedir'] ) . 'testopti.png';
			exec( $path . ' -s3 -q ' . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_IMAGES_PATH . 'testorig.png' ) . ' ' . ewww_image_optimizer_escapeshellarg( $testpng ) );
			$testpngsize = ewww_image_optimizer_filesize( $testpng );
			ewwwio_debug_message( "blind testing pngout, is $testpngsize smaller than 110?" );
			if ( $testpngsize ) {
				unlink( $testpng );
			}
			if ( 0 < $testpngsize && $testpngsize < 110 ) {
				ewwwio_debug_message( 'optimizer found' );
				return esc_html__( 'unknown', 'ewww-image-optimizer' );
			}
			break;
		case 'q': // pngquant.
			exec( $path . ' -V 2>&1', $pngquant_version );
			if ( ewww_image_optimizer_iterable( $pngquant_version ) ) {
				ewwwio_debug_message( "$path: {$pngquant_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $pngquant_version ) && substr( $pngquant_version[0], 0, 3 ) >= 2.0 ) {
				ewwwio_debug_message( 'optimizer found' );
				return $pngquant_version[0];
			}
			break;
		case 'qb':
			$upload_dir = wp_upload_dir();
			$testpng    = trailingslashit( $upload_dir['basedir'] ) . 'testopti.png';
			exec( $path . ' -o ' . ewww_image_optimizer_escapeshellarg( $testpng ) . ' ' . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_IMAGES_PATH . 'testorig.png' ) );
			$testpngsize = ewww_image_optimizer_filesize( $testpng );
			ewwwio_debug_message( "blind testing pngquant, is $testpngsize smaller than 114?" );
			if ( $testpngsize ) {
				unlink( $testpng );
			}
			if ( 0 < $testpngsize && $testpngsize < 114 ) {
				ewwwio_debug_message( 'optimizer found' );
				return esc_html__( 'unknown', 'ewww-image-optimizer' );
			}
			break;
		case 'i': // ImageMagick.
			exec( "$path -version 2>&1", $convert_version );
			if ( ewww_image_optimizer_iterable( $convert_version ) ) {
				ewwwio_debug_message( "$path: {$convert_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $convert_version ) && strpos( $convert_version[0], 'ImageMagick' ) ) {
				ewwwio_debug_message( 'imagemagick found' );
				return $convert_version[0];
			}
			break;
		case 'f': // file.
			exec( "$path -v 2>&1", $file_version );
			if ( ewww_image_optimizer_iterable( $file_version ) ) {
				ewwwio_debug_message( "$path: {$file_version[1]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $file_version[1] ) && preg_match( '/magic/', $file_version[1] ) ) {
				ewwwio_debug_message( 'file binary found' );
				return $file_version[0];
			} elseif ( ! empty( $file_version[1] ) && preg_match( '/usage: file/', $file_version[1] ) ) {
				ewwwio_debug_message( 'file binary found' );
				return $file_version[0];
			}
			break;
		case 'n': // nice.
			exec( "$path 2>&1", $nice_output );
			if ( ewww_image_optimizer_iterable( $nice_output ) && isset( $nice_output[0] ) ) {
				ewwwio_debug_message( "$path: {$nice_output[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( is_array( $nice_output ) && isset( $nice_output[0] ) && preg_match( '/usage/', $nice_output[0] ) ) {
				ewwwio_debug_message( 'nice found' );
				return true;
			} elseif ( is_array( $nice_output ) && isset( $nice_output[0] ) && preg_match( '/^\d+$/', $nice_output[0] ) ) {
				ewwwio_debug_message( 'nice found' );
				return true;
			}
			break;
		case 't': // tar.
			exec( "$path --version 2>&1", $tar_version );
			if ( ewww_image_optimizer_iterable( $tar_version ) ) {
				ewwwio_debug_message( "$path: {$tar_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $tar_version[0] ) && preg_match( '/bsdtar/', $tar_version[0] ) ) {
				ewwwio_debug_message( 'tar found' );
				return $tar_version[0];
			} elseif ( ! empty( $tar_version[0] ) && preg_match( '/GNU tar/i', $tar_version[0] ) ) {
				ewwwio_debug_message( 'tar found' );
				return $tar_version[0];
			}
			break;
		case 'w': // cwebp.
			exec( "$path -version 2>&1", $webp_version );
			if ( ewww_image_optimizer_iterable( $webp_version ) ) {
				ewwwio_debug_message( "$path: {$webp_version[0]}" );
			} else {
				ewwwio_debug_message( "$path: invalid output" );
				break;
			}
			if ( ! empty( $webp_version ) && preg_match( '/0.\d.\d/', $webp_version[0] ) ) {
				ewwwio_debug_message( 'optimizer found' );
				return $webp_version[0];
			}
			break;
		case 'wb':
			$upload_dir = wp_upload_dir();
			$testpng    = trailingslashit( $upload_dir['basedir'] ) . 'testopti.png';
			exec( $path . ' -lossless -quiet ' . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_IMAGES_PATH . 'testorig.png' ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $testpng ) );
			$testpngsize = ewww_image_optimizer_filesize( $testpng );
			ewwwio_debug_message( "blind testing cwebp, is $testpngsize smaller than 114?" );
			if ( $testpngsize ) {
				unlink( $testpng );
			}
			if ( 0 < $testpngsize && $testpngsize < 114 ) {
				ewwwio_debug_message( 'optimizer found' );
				return esc_html__( 'unknown', 'ewww-image-optimizer' );
			}
			break;
	} // End switch().
	ewwwio_debug_message( 'tool not found' );
	ewwwio_memory( __FUNCTION__ );
	return false;
}

/**
 * Searches for the given $binary on a Windows system.
 *
 * Checks the bundled tool, as well as -custom and -alt suffixes, and looks for a system-installed
 * executable if all else fails.
 *
 * @param string $binary Path to the executable.
 * @param string $switch Passed along to ewww_image_optimizer_tool_found().
 * @return string A validated executable path.
 */
function ewww_image_optimizer_find_win_binary( $binary, $switch ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	if ( empty( $binary ) || empty( $switch ) ) {
		return '';
	}
	$use_system = ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_bundle' );
	$tool_path  = trailingslashit( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
	if ( file_exists( $tool_path . $binary . '.exe' ) && ! $use_system ) {
		$binary_path = $tool_path . $binary . '.exe';
		ewwwio_debug_message( "found $binary_path, testing..." );
		if ( ewww_image_optimizer_md5check( $binary_path ) && ewww_image_optimizer_tool_found( '"' . $binary_path . '"', $switch ) ) {
			return '"' . $binary_path . '"';
		}
	}
	if ( file_exists( $tool_path . $binary . '-custom.exe' ) && ! $use_system ) {
		$binary_path = $tool_path . $binary . '-custom.exe';
		ewwwio_debug_message( "found $binary_path, testing..." );
		if ( ewww_image_optimizer_tool_found( '"' . $binary_path . '"', $switch ) ) {
			return '"' . $binary_path . '"';
		}
	}
	if ( file_exists( $tool_path . $binary . '-alt.exe' ) && ! $use_system ) {
		$binary_path = $tool_path . $binary . '-alt.exe';
		ewwwio_debug_message( "found $binary_path, testing..." );
		if ( ewww_image_optimizer_tool_found( '"' . $binary_path . '"', $switch ) ) {
			return '"' . $binary_path . '"';
		}
	}
	// If we still haven't found a usable binary, try a system-installed version.
	if ( ewww_image_optimizer_tool_found( $binary . '.exe', $switch ) ) {
		return $binary . '.exe';
	} else {
		return '';
	}
}

/**
 * Searches for the given $binary on a *nix system.
 *
 * Checks the bundled tool, as well as -custom and -alt suffixes, searches several system folders,
 * and looks for a system-installed binary if all else fails.
 *
 * @param string $binary Path to the binary.
 * @param string $switch Passed along to ewww_image_optimizer_tool_found().
 * @return string A validated binary path.
 */
function ewww_image_optimizer_find_nix_binary( $binary, $switch ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	if ( empty( $binary ) || empty( $switch ) ) {
		return '';
	}
	$use_system = ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_bundle' );
	$tool_path  = trailingslashit( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
	// First check for the binary in the ewww tool folder.
	if ( file_exists( $tool_path . $binary ) && ! $use_system ) {
		$binary_path = $tool_path . $binary;
		ewwwio_debug_message( "found $binary_path, testing..." );
		if ( ewww_image_optimizer_md5check( $binary_path ) && ewww_image_optimizer_mimetype( $binary_path, 'b' ) ) {
			$binary_path = ewww_image_optimizer_escapeshellcmd( $binary_path );
			if ( ewww_image_optimizer_tool_found( $binary_path, $switch ) ) {
				return $binary_path;
			}
		}
	}
	// If the standard binary didn't work, see if the user custom compiled one and check that.
	if ( file_exists( $tool_path . $binary . '-custom' ) && ! $use_system ) {
		$binary_path = $tool_path . $binary . '-custom';
		ewwwio_debug_message( "found $binary_path, testing..." );
		if ( filesize( $binary_path ) > 15000 && ewww_image_optimizer_mimetype( $binary_path, 'b' ) ) {
			$binary_path = ewww_image_optimizer_escapeshellcmd( $binary_path );
			if ( ewww_image_optimizer_tool_found( $binary_path, $switch ) ) {
				return $binary_path;
			}
		}
	}
	// See if the alternative binary works.
	if ( file_exists( $tool_path . $binary . '-alt' ) && ! $use_system ) {
		$binary_path = $tool_path . $binary . '-alt';
		ewwwio_debug_message( "found $binary_path, testing..." );
		if ( filesize( $binary_path ) > 15000 && ewww_image_optimizer_mimetype( $binary_path, 'b' ) ) {
			$binary_path = ewww_image_optimizer_escapeshellcmd( $binary_path );
			if ( ewww_image_optimizer_tool_found( $binary_path, $switch ) ) {
				return $binary_path;
			}
		}
	}
	// If we still haven't found a usable binary, try a system-installed version.
	if ( ewww_image_optimizer_tool_found( $binary, $switch ) ) {
		return $binary;
	} elseif ( ewww_image_optimizer_tool_found( '/usr/bin/' . $binary, $switch ) ) {
		return '/usr/bin/' . $binary;
	} elseif ( ewww_image_optimizer_tool_found( '/usr/local/bin/' . $binary, $switch ) ) {
		return '/usr/local/bin/' . $binary;
	} elseif ( ewww_image_optimizer_tool_found( '/usr/gnu/bin/' . $binary, $switch ) ) {
		return '/usr/gnu/bin/' . $binary;
	} elseif ( ewww_image_optimizer_tool_found( '/usr/syno/bin/' . $binary, $switch ) ) { // For synology diskstation OS.
		return '/usr/syno/bin/' . $binary;
	} else {
		return '';
	}
}

/**
 * Automatically corrects JPG rotation using local jpegtran tool.
 *
 * @param string $file Name of the file to fix.
 * @param string $type File type of the file.
 * @param int    $orientation The EXIF orientation value.
 *
 * @return bool True if the rotation was successful.
 */
function ewww_image_optimizer_jpegtran_autorotate( $file, $type, $orientation ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	if ( 'image/jpeg' != $type ) {
		ewwwio_debug_message( 'not a JPG, go away!' );
		return false;
	}
	if ( ! $orientation || 1 == $orientation ) {
		return false;
	}
	if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) || ! EWWW_IMAGE_OPTIMIZER_CLOUD ) {
		ewww_image_optimizer_define_noexec();
		if ( EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
			$nice = '';
		} else {
			// Check to see if 'nice' exists.
			$nice = ewww_image_optimizer_find_nix_binary( 'nice', 'n' );
		}
	}
	$tools = ewww_image_optimizer_path_check(
		true,
		false,
		false,
		false,
		false,
		false
	);
	if ( empty( $tools['JPEGTRAN'] ) ) {
		return false;
	}
	switch ( $orientation ) {
		case 1:
			return false;
		case 2:
			$transform = '-flip horizontal';
			break;
		case 3:
			$transform = '-rotate 180';
			break;
		case 4:
			$transform = '-flip vertical';
			break;
		case 5:
			$transform = '-transpose';
			break;
		case 6:
			$transform = '-rotate 90';
			break;
		case 7:
			$transform = '-transverse';
			break;
		case 8:
			$transform = '-rotate 270';
			break;
		default:
			return false;
	}
	$outfile = "$file.rotate";
	// Run jpegtran.
	exec( "$nice {$tools['JPEGTRAN']} -trim -copy all $transform -outfile " . ewww_image_optimizer_escapeshellarg( $outfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $file ) );
	ewwwio_debug_message( "$file rotated to $outfile" );

	if ( file_exists( $outfile ) ) {
		$new_type = ewww_image_optimizer_mimetype( $outfile, 'i' );
		// Check the filesize of the new JPG.
		$new_size = filesize( $outfile );
		ewwwio_debug_message( "$outfile exists, testing type and size" );
	} else {
		return false;
	}

	if ( 0 == $new_size || 'image/jpeg' != $new_type ) {
		unlink( $outfile );
		return false;
	}
	ewwwio_debug_message( 'rotation success' );
	rename( $outfile, $file );
	return true;
}

/**
 * Process an image.
 *
 * @param string $file Full absolute path to the image file.
 * @param int    $gallery_type 1=WordPress, 2=nextgen, 3=flagallery, 4=aux_images, 5=image editor,
 *                             6=imagestore.
 * @param bool   $converted True if this is a resize and the full image was converted to a
 *                          new format.
 * @param bool   $new True if this is a new image, so it should attempt conversion regardless of
 *                    previous results.
 * @param bool   $fullsize True if this is a full size (original) image.
 * @return array {
 *     Status of the optimization attempt.
 *
 *     @type string $file The filename or false on error.
 *     @type string $results The results of the optimization.
 *     @type bool $converted True if an image changes formats.
 *     @type string The original filename if converted.
 * }
 */
function ewww_image_optimizer( $file, $gallery_type = 4, $converted = false, $new = false, $fullsize = false ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	session_write_close();
	if ( function_exists( 'wp_raise_memory_limit' ) ) {
		wp_raise_memory_limit( 'image' );
	}
	// If the plugin gets here without initializing, we need to run through some things first.
	if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_CLOUD' ) ) {
		ewww_image_optimizer_cloud_init();
	}
	$bypass_optimization = apply_filters( 'ewww_image_optimizer_bypass', false, $file );
	if ( true === $bypass_optimization ) {
		ewwwio_debug_message( "optimization bypassed: $file" );
		// Tell the user optimization was skipped.
		return array( false, __( 'Optimization skipped', 'ewww-image-optimizer' ), $converted, $file );
	}
	global $s3_uploads_image;
	if ( empty( $s3_uploads_image ) ) {
		$s3_uploads_image = false;
	}
	if ( strpos( $file, 's3' ) === 0 && class_exists( 'S3_Uploads' ) ) {
		$s3_uploads_image    = $file;
		$s3_uploads_instance = S3_Uploads::get_instance();
		$s3_uploads_instance->setup();
		if ( ! function_exists( 'wp_tempnam' ) ) {
			require_once( ABSPATH . 'wp-admin/includes/admin.php' );
		}
		$file = $s3_uploads_instance->copy_image_from_s3( $file );
	}
	// Initialize the original filename.
	$original = $file;
	$result   = '';
	if ( false === is_file( $file ) ) {
		/* translators: %s: Image filename */
		$msg = sprintf( __( 'Could not find %s', 'ewww-image-optimizer' ), $file );
		ewwwio_debug_message( "file doesn't appear to exist: $file" );
		if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
			unlink( $file );
			unset( $s3_uploads_image );
		}
		return array( false, $msg, $converted, $original );
	}
	if ( false === is_writable( $file ) ) {
		/* translators: %s: Image filename */
		$msg = sprintf( __( '%s is not writable', 'ewww-image-optimizer' ), $file );
		ewwwio_debug_message( "couldn't write to the file $file" );
		if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
			unlink( $file );
			unset( $s3_uploads_image );
		}
		return array( false, $msg, $converted, $original );
	}
	$file_perms = 'unknown';
	if ( ewww_image_optimizer_function_exists( 'fileperms' ) ) {
		$file_perms = substr( sprintf( '%o', fileperms( $file ) ), -4 );
	}
	$file_owner = 'unknown';
	$file_group = 'unknown';
	if ( function_exists( 'posix_getpwuid' ) ) {
		$file_owner = posix_getpwuid( fileowner( $file ) );
		$file_owner = $file_owner['name'];
	}
	if ( function_exists( 'posix_getgrgid' ) ) {
		$file_group = posix_getgrgid( filegroup( $file ) );
		$file_group = $file_group['name'];
	}
	ewwwio_debug_message( "permissions: $file_perms, owner: $file_owner, group: $file_group" );
	$type = ewww_image_optimizer_mimetype( $file, 'i' );
	if ( ! $type ) {
		ewwwio_debug_message( 'could not find any functions for mimetype detection' );
		// Otherwise we store an error message since we couldn't get the mime-type.
		if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
			unlink( $file );
			unset( $s3_uploads_image );
		}
		return array( false, __( 'Unknown file type', 'ewww-image-optimizer' ), $converted, $original );
	}
	// Not an image or pdf.
	if ( strpos( $type, 'image' ) === false && strpos( $type, 'pdf' ) === false ) {
		ewwwio_debug_message( "unsupported mimetype: $type" );
		if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
			unlink( $file );
			unset( $s3_uploads_image );
		}
		return array( false, __( 'Unsupported file type', 'ewww-image-optimizer' ) . ": $type", $converted, $original );
	}
	if ( ! EWWW_IMAGE_OPTIMIZER_CLOUD ) {
		ewww_image_optimizer_define_noexec();
		if ( EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
			$nice = '';
		} else {
			// Check to see if 'nice' exists.
			$nice = ewww_image_optimizer_find_nix_binary( 'nice', 'n' );
		}
	}
	$skip = ewww_image_optimizer_skip_tools();
	if ( EWWW_IMAGE_OPTIMIZER_CLOUD ) {
		$skip['jpegtran'] = true;
		$skip['optipng']  = true;
		$skip['gifsicle'] = true;
		$skip['pngout']   = true;
		$skip['pngquant'] = true;
		$skip['webp']     = true;
	}
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_skip_full' ) && $fullsize ) {
		$keep_metadata = true;
	} else {
		$keep_metadata = false;
	}
	if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_skip_full' ) && $fullsize ) {
		$skip_lossy = true;
	} else {
		$skip_lossy = false;
	}
	if ( ini_get( 'max_execution_time' ) < 90 && ewww_image_optimizer_stl_check() ) {
		set_time_limit( 0 );
	}
	// If the full-size image was converted.
	if ( $converted ) { // TODO: remove this block in a future release, it should not fire anymore, as resizes will be converted only directly after the full-size is converted.
		ewwwio_debug_message( 'full-size image was converted, need to rebuild filename for meta' );
		$filenum = $converted;
		// Grab the file extension.
		preg_match( '/\.\w+$/', $file, $fileext );
		// Strip the file extension.
		$filename = str_replace( $fileext[0], '', $file );
		// Grab the dimensions.
		preg_match( '/-\d+x\d+(-\d+)*$/', $filename, $fileresize );
		// Strip the dimensions.
		$filename = str_replace( $fileresize[0], '', $filename );
		// Reconstruct the filename with the same increment (stored in $converted) as the full version.
		$refile = $filename . '-' . $filenum . $fileresize[0] . $fileext[0];
		// Rename the file.
		rename( $file, $refile );
		ewwwio_debug_message( "moved $file to $refile" );
		// And set $file to the new filename.
		$file     = $refile;
		$original = $file;
	}
	// Get the original image size.
	$orig_size = ewww_image_optimizer_filesize( $file );
	ewwwio_debug_message( "original filesize: $orig_size" );
	if ( $orig_size < ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) ) {
		ewwwio_debug_message( "optimization bypassed due to filesize: $file" );
		// Tell the user optimization was skipped.
		if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
			unlink( $file );
			unset( $s3_uploads_image );
		}
		return array( false, __( 'Optimization skipped', 'ewww-image-optimizer' ), $converted, $file );
	}
	if ( 'image/png' == $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) && $orig_size > ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) ) {
		ewwwio_debug_message( "optimization bypassed due to filesize: $file" );
		// Tell the user optimization was skipped.
		if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
			unlink( $file );
			unset( $s3_uploads_image );
		}
		return array( false, __( 'Optimization skipped', 'ewww-image-optimizer' ), $converted, $file );
	}
	$backup_hash = '';
	$new_size    = 0;
	// Set the optimization process to OFF.
	$optimize = false;
	// Toggle the convert process to ON.
	$convert = true;
	// Allow other plugins to mangle the image however they like prior to optimization.
	do_action( 'ewww_image_optimizer_pre_optimization', $file, $type, $fullsize );
	// Run the appropriate optimization/conversion for the mime-type.
	switch ( $type ) {
		case 'image/jpeg':
			$png_size = 0;
			// If jpg2png conversion is enabled, and this image is in the WordPress media library.
			if ( ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' ) && 1 == $gallery_type ) || ! empty( $_REQUEST['ewww_convert'] ) ) {
				// Generate the filename for a PNG:
				// If this is a resize version.
				if ( $converted ) {
					// just change the file extension.
					$pngfile = preg_replace( '/\.\w+$/', '.png', $file );
				} else {
					// If this is a full size image.
					// Get a unique filename for the png image.
					list( $pngfile, $filenum ) = ewww_image_optimizer_unique_filename( $file, '.png' );
				}
			} else {
				// Otherwise, turn conversion OFF.
				$convert = false;
				$pngfile = '';
			}
			// Check for previous optimization, so long as the force flag is not on and this isn't a new image that needs converting.
			if ( empty( $_REQUEST['ewww_force'] ) && ! ( $new && $convert ) ) {
				$results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
				if ( $results_msg ) {
					if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
						unlink( $file );
						$file = $s3_uploads_image;
						unset( $s3_uploads_image );
					}
					return array( $file, $results_msg, $converted, $original );
				}
			}
			if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) > 10 ) {
				list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type, $convert, $pngfile, 'image/png', $skip_lossy );
				if ( $converted ) {
					// Check to see if the user wants the originals deleted.
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
						// Delete the original JPG.
						unlink( $original );
					}
					$converted = $filenum;
					ewww_image_optimizer_webp_create( $file, $new_size, 'image/png', null, $orig_size != $new_size );
				} else {
					ewww_image_optimizer_webp_create( $file, $new_size, $type, null, $orig_size != $new_size );
				}
				break;
			}
			// If we get this far, we are using local (jpegtran) optimization, so do an autorotate on the image.
			ewww_image_optimizer_autorotate( $file );
			// Get the (possibly new) original image size.
			$orig_size = ewww_image_optimizer_filesize( $file );
			if ( $convert ) {
				$tools = ewww_image_optimizer_path_check(
					! $skip['jpegtran'],
					! $skip['optipng'],
					false,
					! $skip['pngout'],
					! $skip['pngquant'],
					! $skip['webp']
				);
			} else {
				$tools = ewww_image_optimizer_path_check(
					! $skip['jpegtran'],
					false,
					false,
					false,
					false,
					! $skip['webp']
				);
			}
			// If jpegtran optimization is disabled.
			if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 0 ) {
				// Store an appropriate message in $result.
				$result = __( 'JPG optimization is disabled', 'ewww-image-optimizer' );
				// Otherwise, if we aren't skipping the utility verification and jpegtran doesn't exist.
			} elseif ( ! $skip['jpegtran'] && ! $tools['JPEGTRAN'] ) {
				/* translators: %s: name of a tool like jpegtran */
				$result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>jpegtran</em>' );
				// Otherwise, things should be good, so...
			} else {
				// Set the optimization process to ON.
				$optimize = true;
			}
			// If optimization is turned ON.
			if ( $optimize ) {
				ewwwio_debug_message( 'attempting to optimize JPG...' );
				// Generate temporary file-name.
				$progfile = $file . '.prog';
				// Check to see if we are supposed to strip metadata (badly named).
				if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) && ! $keep_metadata ) {
					// Don't copy metadata.
					$copy_opt = 'none';
				} else {
					// Copy all the metadata.
					$copy_opt = 'all';
				}
				if ( $orig_size > 10240 ) {
					$progressive = '-progressive';
				} else {
					$progressive = '';
				}
				// Run jpegtran.
				exec( "$nice " . $tools['JPEGTRAN'] . " -copy $copy_opt -optimize $progressive -outfile " . ewww_image_optimizer_escapeshellarg( $progfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $file ) );
				// Check the filesize of the new JPG.
				$new_size = ewww_image_optimizer_filesize( $progfile );
				ewwwio_debug_message( "optimized JPG size: $new_size" );
				// If the best-optimized is smaller than the original JPG, and we didn't create an empty JPG.
				if ( $orig_size > $new_size && 0 != $new_size && ewww_image_optimizer_mimetype( $progfile, 'i' ) == $type ) {
					// Replace the original with the optimized file.
					rename( $progfile, $file );
					// Store the results of the optimization.
					$result = "$orig_size vs. $new_size";
					// If the optimization didn't produce a smaller JPG.
				} else {
					if ( is_file( $progfile ) ) {
						// Delete the optimized file.
						unlink( $progfile );
					}
					// Store the results.
					$result   = 'unchanged';
					$new_size = $orig_size;
				}
			} elseif ( ! $convert ) {
				// If conversion and optimization are both turned OFF, finish the JPG processing.
				ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tools['CWEBP'] );
				break;
			} // End if().
			// If the conversion process is turned ON, or if this is a resize and the full-size was converted.
			if ( $convert ) {
				ewwwio_debug_message( "attempting to convert JPG to PNG: $pngfile" );
				if ( empty( $new_size ) ) {
					$new_size = $orig_size;
				}
				// Convert the JPG to PNG.
				if ( ewww_image_optimizer_gmagick_support() ) {
					try {
						$gmagick = new Gmagick( $file );
						$gmagick->stripimage();
						$gmagick->setimageformat( 'PNG' );
						$gmagick->writeimage( $pngfile );
					} catch ( Exception $gmagick_error ) {
						ewwwio_debug_message( $gmagick_error->getMessage() );
					}
					$png_size = ewww_image_optimizer_filesize( $pngfile );
				}
				if ( ! $png_size && ewww_image_optimizer_imagick_support() ) {
					try {
						$imagick = new Imagick( $file );
						$imagick->stripImage();
						$imagick->setImageFormat( 'PNG' );
						$imagick->writeImage( $pngfile );
					} catch ( Exception $imagick_error ) {
						ewwwio_debug_message( $imagick_error->getMessage() );
					}
					$png_size = ewww_image_optimizer_filesize( $pngfile );
				}
				if ( ! $png_size && ewww_image_optimizer_gd_support() ) {
					ewwwio_debug_message( 'converting with GD' );
					imagepng( imagecreatefromjpeg( $file ), $pngfile );
					$png_size = ewww_image_optimizer_filesize( $pngfile );
				}
				// If lossy optimization is ON and full-size exclusion is not active.
				if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 && $tools['PNGQUANT'] && ! $skip_lossy ) {
					ewwwio_debug_message( 'attempting lossy reduction' );
					exec( "$nice " . $tools['PNGQUANT'] . ' ' . ewww_image_optimizer_escapeshellarg( $pngfile ) );
					$quantfile = preg_replace( '/\.\w+$/', '-fs8.png', $pngfile );
					if ( is_file( $quantfile ) && filesize( $pngfile ) > filesize( $quantfile ) ) {
						ewwwio_debug_message( 'lossy reduction is better: original - ' . filesize( $pngfile ) . ' vs. lossy - ' . filesize( $quantfile ) );
						rename( $quantfile, $pngfile );
					} elseif ( is_file( $quantfile ) ) {
						ewwwio_debug_message( 'lossy reduction is worse: original - ' . filesize( $pngfile ) . ' vs. lossy - ' . filesize( $quantfile ) );
						unlink( $quantfile );
					} else {
						ewwwio_debug_message( 'pngquant did not produce any output' );
					}
				}
				// If optipng isn't disabled.
				if ( $tools['OPTIPNG'] ) {
					// Retrieve the optipng optimization level.
					$optipng_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' );
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) && preg_match( '/0.7/', ewww_image_optimizer_tool_found( $tools['OPTIPNG'], 'o' ) ) && ! $keep_metadata ) {
						$strip = '-strip all ';
					} else {
						$strip = '';
					}
					// If the PNG file was created.
					if ( file_exists( $pngfile ) ) {
						ewwwio_debug_message( 'optimizing converted PNG with optipng' );
						// Run optipng on the new PNG.
						exec( "$nice " . $tools['OPTIPNG'] . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $pngfile ) );
					}
				}
				// If pngout isn't disabled.
				if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) ) {
					// Retrieve the pngout optimization level.
					$pngout_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' );
					// If the PNG file was created.
					if ( file_exists( $pngfile ) ) {
						ewwwio_debug_message( 'optimizing converted PNG with pngout' );
						// Run pngout on the new PNG.
						exec( "$nice " . $tools['PNGOUT'] . " -s$pngout_level -q " . ewww_image_optimizer_escapeshellarg( $pngfile ) );
					}
				}
				$png_size = ewww_image_optimizer_filesize( $pngfile );
				ewwwio_debug_message( "converted PNG size: $png_size" );
				// If the PNG is smaller than the original JPG, and we didn't end up with an empty file.
				if ( $new_size > $png_size && 0 != $png_size && ewww_image_optimizer_mimetype( $pngfile, 'i' ) == 'image/png' ) {
					ewwwio_debug_message( "converted PNG is better: $png_size vs. $new_size" );
					// Store the size of the converted PNG.
					$new_size = $png_size;
					// Check to see if the user wants the originals deleted.
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
						// Delete the original JPG.
						unlink( $file );
					}
					// Store the location of the PNG file.
					$file = $pngfile;
					// Let webp know what we're dealing with now.
					$type = 'image/png';
					// Successful conversion and we store the increment.
					$converted = $filenum;
				} else {
					ewwwio_debug_message( 'converted PNG is no good' );
					// Otherwise delete the PNG.
					$converted = false;
					if ( is_file( $pngfile ) ) {
						unlink( $pngfile );
					}
				}
			} // End if().
			ewww_image_optimizer_webp_create( $file, $new_size, $type, $tools['CWEBP'], $orig_size != $new_size );
			break;
		case 'image/png':
			$jpg_size = 0;
			// Png2jpg conversion is turned on, and the image is in the WordPress media library.
			// We check for transparency later, after optimization, because optipng might fix an empty alpha channel.
			if ( ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' ) || ! empty( $_REQUEST['ewww_convert'] ) )
				&& 1 == $gallery_type && ! $skip_lossy ) {
				ewwwio_debug_message( 'PNG to JPG conversion turned on' );
				$cloud_background = '';
				$r                = '';
				$g                = '';
				$b                = '';
				// If the user set a fill background for transparency.
				$background = ewww_image_optimizer_jpg_background();
				if ( $background ) {
					$cloud_background = "#$background";
					// Set background color for GD.
					$r = hexdec( '0x' . strtoupper( substr( $background, 0, 2 ) ) );
					$g = hexdec( '0x' . strtoupper( substr( $background, 2, 2 ) ) );
					$b = hexdec( '0x' . strtoupper( substr( $background, 4, 2 ) ) );
					// Set the background flag for 'convert'.
					$background = '-background ' . '"' . "#$background" . '"';
				}
				$cquality = '';
				$gquality = '92';
				// If the user manually set the JPG quality.
				$quality = ewww_image_optimizer_jpg_quality();
				if ( $quality ) {
					// Set the quality for GD.
					$gquality = $quality;
					// Set the quality flag for 'convert'.
					$cquality = "-quality $quality";
				}
				// If this is a resize version.
				if ( $converted ) {
					// Just replace the file extension with a .jpg.
					$jpgfile = preg_replace( '/\.\w+$/', '.jpg', $file );
					// If this is a full version.
				} else {
					// Construct the filename for the new JPG.
					list( $jpgfile, $filenum ) = ewww_image_optimizer_unique_filename( $file, '.jpg' );
				}
			} else {
				ewwwio_debug_message( 'PNG to JPG conversion turned off' );
				// Turn the conversion process OFF.
				$convert          = false;
				$jpgfile          = '';
				$r                = null;
				$g                = null;
				$b                = null;
				$cloud_background = '';
				$gquality         = null;
			} // End if().
			// Check for previous optimization, so long as the force flag is on and this isn't a new image that needs converting.
			if ( empty( $_REQUEST['ewww_force'] ) && ! ( $new && $convert ) ) {
				$results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
				if ( $results_msg ) {
					if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
						unlink( $file );
						$file = $s3_uploads_image;
						unset( $s3_uploads_image );
					}
					return array( $file, $results_msg, $converted, $original );
				}
			}
			if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) >= 20 && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
				list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer(
					$file,
					$type,
					$convert,
					$jpgfile,
					'image/jpeg',
					$skip_lossy,
					$cloud_background,
					$gquality
				);
				if ( $converted ) {
					// Check to see if the user wants the originals deleted.
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
						// Delete the original JPG.
						unlink( $original );
					}
					$converted = $filenum;
					ewww_image_optimizer_webp_create( $file, $new_size, 'image/jpeg', null, $orig_size != $new_size );
				} else {
					ewww_image_optimizer_webp_create( $file, $new_size, $type, null, $orig_size != $new_size );
				}
				break;
			}
			if ( $convert ) {
				$tools = ewww_image_optimizer_path_check(
					! $skip['jpegtran'],
					! $skip['optipng'],
					false,
					! $skip['pngout'],
					! $skip['pngquant'],
					! $skip['webp']
				);
			} else {
				$tools = ewww_image_optimizer_path_check(
					false,
					! $skip['optipng'],
					false,
					! $skip['pngout'],
					! $skip['pngquant'],
					! $skip['webp']
				);
			}
			// If png optimization is disabled.
			if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 0 ) {
				// Tell the user all PNG tools are disabled.
				$result = __( 'PNG optimization is disabled', 'ewww-image-optimizer' );
				// If the utility checking is on, optipng is enabled, but optipng cannot be found.
			} elseif ( ! $skip['optipng'] && ! $tools['OPTIPNG'] ) {
				/* translators: %s: name of a tool like jpegtran */
				$result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>optipng</em>' );
				// If the utility checking is on, pngout is enabled, but pngout cannot be found.
			} elseif ( ! $skip['pngout'] && ! $tools['PNGOUT'] ) {
				/* translators: %s: name of a tool like jpegtran */
				$result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>pngout</em>' );
			} else {
				// Turn optimization on if we made it through all the checks.
				$optimize = true;
			}
			// If optimization is turned on.
			if ( $optimize ) {
				// If lossy optimization is ON and full-size exclusion is not active.
				if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) == 40 && $tools['PNGQUANT'] && ! $skip_lossy ) {
					ewwwio_debug_message( 'attempting lossy reduction' );
					exec( "$nice " . $tools['PNGQUANT'] . ' ' . ewww_image_optimizer_escapeshellarg( $file ) );
					$quantfile = preg_replace( '/\.\w+$/', '-fs8.png', $file );
					if ( is_file( $quantfile ) && filesize( $file ) > filesize( $quantfile ) && ewww_image_optimizer_mimetype( $quantfile, 'i' ) == $type ) {
						ewwwio_debug_message( 'lossy reduction is better: original - ' . filesize( $file ) . ' vs. lossy - ' . filesize( $quantfile ) );
						rename( $quantfile, $file );
					} elseif ( is_file( $quantfile ) ) {
						ewwwio_debug_message( 'lossy reduction is worse: original - ' . filesize( $file ) . ' vs. lossy - ' . filesize( $quantfile ) );
						unlink( $quantfile );
					} else {
						ewwwio_debug_message( 'pngquant did not produce any output' );
					}
				}
				$tempfile = $file . '.tmp.png';
				copy( $file, $tempfile );
				// If optipng is enabled.
				if ( $tools['OPTIPNG'] ) {
					// Retrieve the optimization level for optipng.
					$optipng_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' );
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) && preg_match( '/0.7/', ewww_image_optimizer_tool_found( $tools['OPTIPNG'], 'o' ) ) && ! $keep_metadata ) {
						$strip = '-strip all ';
					} else {
						$strip = '';
					}
					// Run optipng on the PNG file.
					exec( "$nice " . $tools['OPTIPNG'] . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $tempfile ) );
				}
				// If pngout is enabled.
				if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) ) {
					// Retrieve the optimization level for pngout.
					$pngout_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' );
					// Run pngout on the PNG file.
					exec( "$nice " . $tools['PNGOUT'] . " -s$pngout_level -q " . ewww_image_optimizer_escapeshellarg( $tempfile ) );
				}
				// Retrieve the filesize of the temporary PNG.
				$new_size = ewww_image_optimizer_filesize( $tempfile );
				// If the new PNG is smaller.
				if ( $orig_size > $new_size && 0 != $new_size && ewww_image_optimizer_mimetype( $tempfile, 'i' ) == $type ) {
					// Replace the original with the optimized file.
					rename( $tempfile, $file );
					// Store the results of the optimization.
					$result = "$orig_size vs. $new_size";
					// If the optimization didn't produce a smaller PNG.
				} else {
					if ( is_file( $tempfile ) ) {
						// Delete the optimized file.
						unlink( $tempfile );
					}
					// Store the results.
					$result   = 'unchanged';
					$new_size = $orig_size;
				}
			} elseif ( ! $convert ) {
				// If conversion and optimization are both disabled we are done here.
				ewwwio_debug_message( 'calling webp, but neither convert or optimize' );
				ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tools['CWEBP'] );
				break;
			} // End if().
			// Retrieve the new filesize of the PNG.
			$new_size = ewww_image_optimizer_filesize( $file );
			// Double check for png2jpg conversion to see if we have an alpha image.
			if ( $convert && ewww_image_optimizer_png_alpha( $file ) && ! ewww_image_optimizer_jpg_background() ) {
				ewwwio_debug_message( 'PNG to JPG conversion turned off due to alpha' );
				$convert = false;
			}
			// If conversion is on and the PNG doesn't have transparency or the user set a background color to replace transparency.
			if ( $convert ) {
				ewwwio_debug_message( "attempting to convert PNG to JPG: $jpgfile" );
				if ( empty( $new_size ) ) {
					$new_size = $orig_size;
				}
				$magick_background = ewww_image_optimizer_jpg_background();
				if ( empty( $magick_background ) ) {
					$magick_background = '000000';
				}
				// Convert the PNG to a JPG with all the proper options.
				if ( ewww_image_optimizer_gmagick_support() ) {
					try {
						if ( ewww_image_optimizer_png_alpha( $file ) ) {
							$gmagick_overlay = new Gmagick( $file );
							$gmagick         = new Gmagick();
							$gmagick->newimage( $gmagick_overlay->getimagewidth(), $gmagick_overlay->getimageheight(), '#' . $magick_background );
							$gmagick->compositeimage( $gmagick_overlay, 1, 0, 0 );
						} else {
							$gmagick = new Gmagick( $file );
						}
						$gmagick->setimageformat( 'JPG' );
						$gmagick->setcompressionquality( $gquality );
						$gmagick->writeimage( $jpgfile );
					} catch ( Exception $gmagick_error ) {
						ewwwio_debug_message( $gmagick_error->getMessage() );
					}
					$jpg_size = ewww_image_optimizer_filesize( $jpgfile );
				}
				if ( ! $jpg_size && ewww_image_optimizer_imagick_support() ) {
					try {
						$imagick = new Imagick( $file );
						if ( ewww_image_optimizer_png_alpha( $file ) ) {
							$imagick->setImageBackgroundColor( new ImagickPixel( '#' . $magick_background ) );
							$imagick->setImageAlphaChannel( 11 );
						}
						$imagick->setImageFormat( 'JPG' );
						$imagick->setCompressionQuality( $gquality );
						$imagick->writeImage( $jpgfile );
					} catch ( Exception $imagick_error ) {
						ewwwio_debug_message( $imagick_error->getMessage() );
					}
					$jpg_size = ewww_image_optimizer_filesize( $jpgfile );
				}
				if ( ! $jpg_size && ewww_image_optimizer_gd_support() ) {
					ewwwio_debug_message( 'converting with GD' );
					// Retrieve the data from the PNG.
					$input = imagecreatefrompng( $file );
					// Retrieve the dimensions of the PNG.
					list($width, $height) = getimagesize( $file );
					// Create a new image with those dimensions.
					$output = imagecreatetruecolor( $width, $height );
					if ( '' === $r ) {
						$r = 255;
						$g = 255;
						$b = 255;
					}
					// Allocate the background color.
					$rgb = imagecolorallocate( $output, $r, $g, $b );
					// Fill the new image with the background color.
					imagefilledrectangle( $output, 0, 0, $width, $height, $rgb );
					// Copy the original image to the new image.
					imagecopy( $output, $input, 0, 0, 0, 0, $width, $height );
					// Output the JPG with the quality setting.
					imagejpeg( $output, $jpgfile, $gquality );
				}
				$jpg_size = ewww_image_optimizer_filesize( $jpgfile );
				if ( $jpg_size ) {
					ewwwio_debug_message( "converted JPG filesize: $jpg_size" );
				} else {
					ewwwio_debug_message( 'unable to convert to JPG' );
				}
				// Next we need to optimize that JPG if jpegtran is enabled.
				if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) == 10 && file_exists( $jpgfile ) ) {
					// Generate temporary file-name.
					$progfile = $jpgfile . '.prog';
					// Check to see if we are supposed to strip metadata (badly named).
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) && ! $keep_metadata ) {
						// Don't copy metadata.
						$copy_opt = 'none';
					} else {
						// Copy all the metadata.
						$copy_opt = 'all';
					}
					if ( $jpg_size > 10240 ) {
						$progressive = '-progressive';
					} else {
						$progressive = '';
					}
					// Run jpegtran.
					exec( "$nice " . $tools['JPEGTRAN'] . " -copy $copy_opt -optimize $progressive -outfile " . ewww_image_optimizer_escapeshellarg( $progfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $jpgfile ) );
					// Check the filesize of the new JPG.
					$opt_jpg_size = ewww_image_optimizer_filesize( $progfile );
					// If the best-optimized is smaller than the original JPG, and we didn't create an empty JPG.
					if ( $jpg_size > $opt_jpg_size && 0 != $opt_jpg_size ) {
						// Replace the original with the optimized file.
						rename( $progfile, $jpgfile );
						// Store the size of the optimized JPG.
						$jpg_size = $opt_jpg_size;
						ewwwio_debug_message( 'optimized JPG was smaller than un-optimized version' );
						// If the optimization didn't produce a smaller JPG.
					} elseif ( is_file( $progfile ) ) {
						unlink( $progfile );
					}
				}
				ewwwio_debug_message( "converted JPG size: $jpg_size" );
				// If the new JPG is smaller than the original PNG.
				if ( $new_size > $jpg_size && 0 != $jpg_size && ewww_image_optimizer_mimetype( $jpgfile, 'i' ) == 'image/jpeg' ) {
					// Store the size of the JPG as the new filesize.
					$new_size = $jpg_size;
					// If the user wants originals delted after a conversion.
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
						// Delete the original PNG.
						unlink( $file );
					}
					// Update the $file location to the new JPG.
					$file = $jpgfile;
					// Let webp know what we're dealing with now.
					$type = 'image/jpeg';
					// Successful conversion, so we store the increment.
					$converted = $filenum;
				} else {
					$converted = false;
					if ( is_file( $jpgfile ) ) {
						// Otherwise delete the new JPG.
						unlink( $jpgfile );
					}
				}
			} // End if().
			ewww_image_optimizer_webp_create( $file, $new_size, $type, $tools['CWEBP'], $orig_size != $new_size );
			break;
		case 'image/gif':
			// If gif2png is turned on, and the image is in the WordPress media library.
			if ( ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_to_png' ) || ! empty( $_REQUEST['ewww_convert'] ) )
				&& 1 == $gallery_type && ! ewww_image_optimizer_is_animated( $file ) ) {
				// Generate the filename for a PNG:
				// if this is a resize version...
				if ( $converted ) {
					// just change the file extension.
					$pngfile = preg_replace( '/\.\w+$/', '.png', $file );
				} else {
					// If this is the full version...
					// construct the filename for the new PNG.
					list( $pngfile, $filenum ) = ewww_image_optimizer_unique_filename( $file, '.png' );
				}
			} else {
				// Turn conversion OFF.
				$convert = false;
				$pngfile = '';
			}
			// Check for previous optimization, so long as the force flag is on and this isn't a new image that needs converting.
			if ( empty( $_REQUEST['ewww_force'] ) && ! ( $new && $convert ) ) {
				$results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
				if ( $results_msg ) {
					if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
						unlink( $file );
						$file = $s3_uploads_image;
						unset( $s3_uploads_image );
					}
					return array( $file, $results_msg, $converted, $original );
				}
			}
			if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) == 10 ) {
				list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type, $convert, $pngfile, 'image/png', $skip_lossy );
				if ( $converted ) {
					// Check to see if the user wants the originals deleted.
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
						// Delete the original GIF.
						unlink( $original );
					}
					$converted = $filenum;
					ewww_image_optimizer_webp_create( $file, $new_size, 'image/png', null, $orig_size != $new_size );
				}
				break;
			}
			if ( $convert ) {
				$tools = ewww_image_optimizer_path_check(
					false,
					! $skip['optipng'],
					! $skip['gifsicle'],
					! $skip['pngout'],
					! $skip['pngquant'],
					! $skip['webp']
				);
			} else {
				$tools = ewww_image_optimizer_path_check(
					false,
					false,
					! $skip['gifsicle'],
					false,
					false,
					false
				);
			}
			// If gifsicle is disabled.
			if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) == 0 ) {
				$result = __( 'GIF optimization is disabled', 'ewww-image-optimizer' );
				// If utility checking is on, and gifsicle is not installed.
			} elseif ( ! $skip['gifsicle'] && ! $tools['GIFSICLE'] ) {
				/* translators: %s: name of a tool like jpegtran */
				$result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>gifsicle</em>' );
			} else {
				// Otherwise, turn optimization ON.
				$optimize = true;
			}
			// If optimization is turned ON.
			if ( $optimize ) {
				$tempfile = $file . '.tmp'; // temporary GIF output.
				// Run gifsicle on the GIF.
				exec( "$nice " . $tools['GIFSICLE'] . " -O3 --careful -o $tempfile " . ewww_image_optimizer_escapeshellarg( $file ) );
				// Retrieve the filesize of the temporary GIF.
				$new_size = ewww_image_optimizer_filesize( $tempfile );
				// If the new GIF is smaller.
				if ( $orig_size > $new_size && 0 != $new_size && ewww_image_optimizer_mimetype( $tempfile, 'i' ) == $type ) {
					// Replace the original with the optimized file.
					rename( $tempfile, $file );
					// Store the results of the optimization.
					$result = "$orig_size vs. $new_size";
					// If the optimization didn't produce a smaller GIF.
				} else {
					if ( is_file( $tempfile ) ) {
						// Delete the optimized file.
						unlink( $tempfile );
					}
					// Store the results.
					$result   = 'unchanged';
					$new_size = $orig_size;
				}
			} elseif ( ! $convert ) {
				// If conversion and optimization are both turned OFF, we are done here.
				break;
			}
			// Get the new filesize for the GIF.
			$new_size = ewww_image_optimizer_filesize( $file );
			// If conversion is ON and the GIF isn't animated.
			if ( $convert && ! ewww_image_optimizer_is_animated( $file ) ) {
				if ( empty( $new_size ) ) {
					$new_size = $orig_size;
				}
				// If optipng is enabled.
				if ( $tools['OPTIPNG'] ) {
					// Retrieve the optipng optimization level.
					$optipng_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' );
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) && preg_match( '/0.7/', ewww_image_optimizer_tool_found( $tools['OPTIPNG'], 'o' ) ) && ! $keep_metadata ) {
						$strip = '-strip all ';
					} else {
						$strip = '';
					}
					// Run optipng on the GIF file.
					exec( "$nice " . $tools['OPTIPNG'] . ' -out ' . ewww_image_optimizer_escapeshellarg( $pngfile ) . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $file ) );
				}
				// If pngout is enabled.
				if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) && $tools['PNGOUT'] ) {
					// Retrieve the pngout optimization level.
					$pngout_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' );
					// If $pngfile exists (which means optipng was run already).
					if ( file_exists( $pngfile ) ) {
						// Run pngout on the PNG file.
						exec( "$nice " . $tools['PNGOUT'] . " -s$pngout_level -q " . ewww_image_optimizer_escapeshellarg( $pngfile ) );
					} else {
						// Run pngout on the GIF file.
						exec( "$nice " . $tools['PNGOUT'] . " -s$pngout_level -q " . ewww_image_optimizer_escapeshellarg( $file ) . ' ' . ewww_image_optimizer_escapeshellarg( $pngfile ) );
					}
				}
				// Retrieve the filesize of the PNG.
				$png_size = ewww_image_optimizer_filesize( $pngfile );
				// If the new PNG is smaller than the original GIF.
				if ( $new_size > $png_size && 0 != $png_size && ewww_image_optimizer_mimetype( $pngfile, 'i' ) == 'image/png' ) {
					// Store the PNG size as the new filesize.
					$new_size = $png_size;
					// If the user wants original GIFs deleted after successful conversion.
					if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) == true ) {
						// Delete the original GIF.
						unlink( $file );
					}
					// Update the $file location with the new PNG.
					$file = $pngfile;
					// Let webp know what we're dealing with now.
					$type = 'image/png';
					// Normally this would be at the end of the section, but we only want to do webp if the image was successfully converted to a png.
					ewww_image_optimizer_webp_create( $file, $new_size, $type, $tools['CWEBP'], $orig_size != $new_size );
					// Successful conversion, so we store the increment.
					$converted = $filenum;
				} else {
					$converted = false;
					if ( is_file( $pngfile ) ) {
						unlink( $pngfile );
					}
				}
			} // End if().
			break;
		case 'application/pdf':
			if ( empty( $_REQUEST['ewww_force'] ) ) {
				$results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
				if ( $results_msg ) {
					if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
						unlink( $file );
						$file = $s3_uploads_image;
						unset( $s3_uploads_image );
					}
					return array( $file, $results_msg, false, $original );
				}
			}
			if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' ) > 0 ) {
				list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type );
			}
			break;
		default:
			// If not a JPG, PNG, or GIF, tell the user we don't work with strangers.
			if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
				unlink( $file );
				unset( $s3_uploads_image );
			}
			return array( false, __( 'Unsupported file type', 'ewww-image-optimizer' ) . ": $type", $converted, $original );
	} // End switch().
	// Allow other plugins to run operations on the images after optimization.
	// NOTE: it is recommended to do any image modifications prior to optimization, otherwise you risk un-optimizing your images here.
	do_action( 'ewww_image_optimizer_post_optimization', $file, $type, $fullsize );
	// If their cloud api license limit has been exceeded.
	if ( 'exceeded' == $result ) {
		if ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
			unlink( $file );
			unset( $s3_uploads_image );
		}
		return array( false, __( 'License exceeded', 'ewww-image-optimizer' ), $converted, $original );
	}
	if ( ! empty( $new_size ) ) {
		// Set correct file permissions.
		$stat = stat( dirname( $file ) );
		ewwwio_debug_message( 'folder mode: ' . $stat['mode'] );
		$perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
		ewwwio_debug_message( "attempting chmod with $perms" );
		chmod( $file, $perms );

		$results_msg = ewww_image_optimizer_update_table( $file, $new_size, $orig_size, $original, $backup_hash );
		if ( $s3_uploads_image && strpos( $file, 's3-uploads' ) === false ) {
			copy( $file, $s3_uploads_image );
			unlink( $file );
			$file = $s3_uploads_image;
			unset( $s3_uploads_image );
		}
		ewwwio_memory( __FUNCTION__ );
		return array( $file, $results_msg, $converted, $original );
	} elseif ( strpos( $file, 's3' ) !== 0 && strpos( $file, 's3-uploads' ) === false && $s3_uploads_image && is_file( $file ) ) {
		unset( $s3_uploads_image );
		unlink( $file );
	}
	ewwwio_memory( __FUNCTION__ );
	// Otherwise, send back the filename, the results (some sort of error message), the $converted flag, and the name of the original image.
	return array( false, $result, $converted, $original );
}

/**
 * Creates webp images alongside JPG and PNG files.
 *
 * @param string $file The name of the JPG/PNG file.
 * @param int    $orig_size The filesize of the JPG/PNG file.
 * @param string $type The mime-type of the incoming file.
 * @param string $tool The path to the cwebp binary, if installed.
 * @param bool   $recreate True to keep the .webp image even if it is larger than the JPG/PNG.
 */
function ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tool, $recreate = false ) {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	// Change the file extension.
	$webpfile    = $file . '.webp';
	$bypass_webp = apply_filters( 'ewww_image_optimizer_bypass_webp', false, $file );
	if ( true === $bypass_webp ) {
		ewwwio_debug_message( "webp generation bypassed: $file" );
		return;
	} elseif ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ) {
		return;
	} elseif ( is_file( $webpfile ) && empty( $_REQUEST['ewww_force'] ) && ! $recreate ) {
		ewwwio_debug_message( 'webp file exists, not forcing or recreating' );
		return;
	}
	if ( empty( $tool ) ) {
		ewww_image_optimizer_cloud_optimizer( $file, $type, false, $webpfile, 'image/webp' );
	} else {
		// Check to see if 'nice' exists.
		$nice = ewww_image_optimizer_find_nix_binary( 'nice', 'n' );
		// Check to see if we are supposed to strip metadata (badly named).
		if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) ) {
			// Don't copy metadata.
			$copy_opt = 'none';
		} else {
			// Copy all the metadata.
			$copy_opt = 'all';
		}
		switch ( $type ) {
			case 'image/jpeg':
				$quality = (int) apply_filters( 'jpeg_quality', 82, 'image/webp' );
				exec( "$nice " . $tool . " -q $quality -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1', $cli_output );
				break;
			case 'image/png':
				exec( "$nice " . $tool . " -lossless -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1', $cli_output );
				break;
		}
	}
	$webp_size = ewww_image_optimizer_filesize( $webpfile );
	ewwwio_debug_message( "webp is $webp_size vs. $type is $orig_size" );
	if ( is_file( $webpfile ) && $orig_size < $webp_size && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
		ewwwio_debug_message( 'webp file was too big, deleting' );
		unlink( $webpfile );
	} elseif ( is_file( $webpfile ) ) {
		// Set correct file permissions.
		$stat  = stat( dirname( $webpfile ) );
		$perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
		chmod( $webpfile, $perms );
	}
	ewwwio_memory( __FUNCTION__ );
}

/**
 * Redirects back to previous page after PNGOUT installation.
 */
function ewww_image_optimizer_install_pngout_wrapper() {
	ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
	$permissions = apply_filters( 'ewww_image_optimizer_admin_permissions', '' );
	if ( false === current_user_can( $permissions ) ) {
		wp_die( esc_html__( 'You do not have permission to install image optimizer utilities.', 'ewww-image-optimizer' ) );
	}
	$sendback = ewww_image_optimizer_install_pngout();
	wp_redirect( esc_url_raw( $sendback ) );
	ewwwio_memory( __FUNCTION__ );
	exit( 0 );
}

/**
 * Installs pngout from the official site.
 *
 * @return string The url from whence we came (settings page), with success or error parameters added.
 */
function ewww_image_optimizer_install_pngout() {
	if ( PHP_OS != 'WINNT' ) {
		$tar = ewww_image_optimizer_find_nix_binary( 'tar', 't' );
	}
	if ( empty( $tar ) && PHP_OS != 'WINNT' ) {
		$pngout_error = __( 'tar command not found', 'ewww-image-optimizer' );
	}
	if ( PHP_OS == 'Linux' ) {
		$os_string = 'linux';
	}
	if ( PHP_OS == 'FreeBSD' ) {
		$os_string = 'bsd';
	}
	$latest    = '20150319';
	$tool_path = trailingslashit( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
	if ( empty( $pngout_error ) ) {
		if ( PHP_OS == 'Linux' || PHP_OS == 'FreeBSD' ) {
			$download_result = ewww_image_optimizer_escapeshellarg( download_url( 'http://static.jonof.id.au/dl/kenutils/pngout-' . $latest . '-' . $os_string . '-static.tar.gz' ) );
			if ( is_wp_error( $download_result ) ) {
				$pngout_error = $download_result->get_error_message();
			} else {
				$arch_type = 'i686';
				if ( ewww_image_optimizer_function_exists( 'php_uname' ) ) {
					$arch_type = php_uname( 'm' );
				}
				exec( "$tar xzf $download_result -C " . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_BINARY_PATH ) . ' pngout-' . $latest . '-' . $os_string . '-static/' . $arch_type . '/pngout-static' );
				if ( file_exists( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-' . $os_string . '-static/' . $arch_type . '/pngout-static' ) ) {
					if ( ! rename( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-' . $os_string . '-static/' . $arch_type . '/pngout-static', $tool_path . 'pngout-static' ) ) {
						if ( empty( $pngout_error ) ) {
							$pngout_error = __( 'could not move pngout', 'ewww-image-optimizer' );
						}
					}
					if ( ! chmod( $tool_path . 'pngout-static', 0755 ) ) {
						if ( empty( $pngout_error ) ) {
							$pngout_error = __( 'could not set permissions', 'ewww-image-optimizer' );
						}
					}
					$pngout_version = ewww_image_optimizer_tool_found( ewww_image_optimizer_escapeshellarg( $tool_path ) . 'pngout-static', 'p' );
				} else {
					$pngout_error = __( 'extraction of files failed', 'ewww-image-optimizer' );
				}
			}
		}
		if ( PHP_OS == 'Darwin' ) {
			$download_result = ewww_image_optimizer_escapeshellarg( download_url( 'http://static.jonof.id.au/dl/kenutils/pngout-' . $latest . '-darwin.tar.gz' ) );
			if ( is_wp_error( $download_result ) ) {
				$pngout_error = $download_result->get_error_message();
			} else {
				exec( "$tar xzf $download_result -C " . ewww_image_optimizer_escapeshellarg( EWWW_IMAGE_OPTIMIZER_BINARY_PATH ) . ' pngout-' . $latest . '-darwin/pngout' );
				if ( file_exists( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-darwin/pngout' ) ) {
					if ( ! rename( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-darwin/pngout', $tool_path . 'pngout-static' ) ) {
						if ( empty( $pngout_error ) ) {
							$pngout_error = __( 'could not move pngout', 'ewww-image-optimizer' );
						}
					}
					if ( ! chmod( $tool_path . 'pngout-static', 0755 ) ) {
						if ( empty( $pngout_error ) ) {
							$pngout_error = __( 'could not set permissions', 'ewww-image-optimizer' );
						}
					}
					$pngout_version = ewww_image_optimizer_tool_found( ewww_image_optimizer_escapeshellarg( $tool_path ) . 'pngout-static', 'p' );
				} else {
					$pngout_error = __( 'extraction of files failed', 'ewww-image-optimizer' );
				}
			}
		}
	} // End if().
	if ( PHP_OS == 'WINNT' ) {
		$download_result = download_url( 'http://advsys.net/ken/util/pngout.exe' );
		if ( is_wp_error( $download_result ) ) {
			$pngout_error = $download_result->get_error_message();
		} else {
			if ( ! rename( $download_result, $tool_path . 'pngout.exe' ) ) {
				if ( empty( $pngout_error ) ) {
					$pngout_error = __( 'could not move pngout', 'ewww-image-optimizer' );
				}
			}
			$pngout_version = ewww_image_optimizer_tool_found( '"' . $tool_path . 'pngout.exe"', 'p' );
		}
	}
	if ( ! empty( $pngout_version ) ) {
		$sendback = add_query_arg( 'ewww_pngout', 'success', remove_query_arg( array( 'ewww_pngout', 'ewww_error' ), wp_get_referer() ) );
	}
	if ( ! isset( $sendback ) ) {
		$sendback = add_query_arg(
			array(
				'ewww_pngout' => 'failed',
				'ewww_error'  => urlencode( $pngout_error ),
			),
			remove_query_arg( array( 'ewww_pngout', 'ewww_error' ), wp_get_referer() )
		);
	}
	return $sendback;
}

/**
 * Removes any binaries that have been installed in the wp-content/ewww/ folder.
 */
function ewww_image_optimizer_remove_binaries() {
	if ( ! class_exists( 'RecursiveIteratorIterator' ) ) {
		return;
	}
	if ( ! is_dir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) {
		return;
	}
	$iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ), RecursiveIteratorIterator::CHILD_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
	foreach ( $iterator as $file ) {
		if ( $file->isFile() ) {
			$path = $file->getPathname();
			if ( is_writable( $path ) ) {
				unlink( $path );
			}
		}
	}
	if ( ! class_exists( 'FilesystemIterator' ) ) {
		return;
	}
	clearstatcache();
	$iterator = new FilesystemIterator( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
	if ( ! $iterator->valid() ) {
		rmdir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
	}
}
