<?php define( 'PHOTON__ALLOW_QUERY_STRINGS', 1 ); require dirname( __FILE__ ) . '/plugin.php'; if ( file_exists( dirname( __FILE__ ) . '/../config.php' ) ) require dirname( __FILE__ ) . '/../config.php'; else if ( file_exists( dirname( __FILE__ ) . '/config.php' ) ) require dirname( __FILE__ ) . '/config.php'; // Explicit Configuration $allowed_functions = apply_filters( 'allowed_functions', array( // 'q' => RESERVED // 'zoom' => global resolution multiplier (argument filter) // 'quality' => sets the quality of JPEG images during processing // 'strip => strips JPEG images of exif, icc or all "extra" data (params: info,color,all) 'h' => 'set_height', // done 'w' => 'set_width', // done 'crop' => 'crop', // done 'resize' => 'resize_and_crop', // done 'fit' => 'fit_in_box', // done 'lb' => 'letterbox', // done 'ulb' => 'unletterbox', // compat 'filter' => 'filter', // compat 'brightness' => 'brightness', // compat 'contrast' => 'contrast', // compat 'colorize' => 'colorize', // compat 'smooth' => 'smooth', // compat ) ); unset( $allowed_functions['q'] ); $allowed_types = apply_filters( 'allowed_types', array( 'gif', 'jpg', 'jpeg', 'png', ) ); $disallowed_file_headers = apply_filters( 'disallowed_file_headers', array( '8BPS', ) ); // Expects a trailing slash $tmpdir = apply_filters( 'tmpdir', '/tmp/' ); $remote_image_max_size = apply_filters( 'remote_image_max_size', 55 * 1024 * 1024 ); /* Array of domains exceptions * Keys are domain name * Values are bitmasks with the following options: * PHOTON__ALLOW_QUERY_STRINGS: Append the string found in the 'q' query string parameter as the query string of the remote URL */ $origin_domain_exceptions = apply_filters( 'origin_domain_exceptions', array() ); define( 'JPG_MAX_QUALITY', 89 ); define( 'PNG_MAX_QUALITY', 80 ); // The 'w' and 'h' parameter are processed distinctly define( 'ALLOW_DIMS_CHAINING', true ); // Strip all meta data from WebP images by default define( 'CWEBP_DEFAULT_META_STRIP', 'all' ); // You can override this by defining it in config.php if ( ! defined( 'UPSCALE_MAX_PIXELS' ) ) define( 'UPSCALE_MAX_PIXELS', 2000 ); // Allow smaller upscales for GIFs, compared to the other image types if ( ! defined( 'UPSCALE_MAX_PIXELS_GIF' ) ) define( 'UPSCALE_MAX_PIXELS_GIF', 1000 ); // Implicit configuration if ( file_exists( '/usr/local/bin/optipng' ) && ! defined( 'DISABLE_IMAGE_OPTIMIZATIONS' ) ) define( 'OPTIPNG', '/usr/local/bin/optipng' ); else define( 'OPTIPNG', false ); if ( file_exists( '/usr/local/bin/pngquant' ) && ! defined( 'DISABLE_IMAGE_OPTIMIZATIONS' ) ) define( 'PNGQUANT', '/usr/local/bin/pngquant' ); else define( 'PNGQUANT', false ); if ( file_exists( '/usr/local/bin/cwebp' ) && ! defined( 'DISABLE_IMAGE_OPTIMIZATIONS' ) ) define( 'CWEBP', '/usr/local/bin/cwebp' ); else define( 'CWEBP', false ); if ( file_exists( '/usr/local/bin/jpegoptim' ) && ! defined( 'DISABLE_IMAGE_OPTIMIZATIONS' ) ) define( 'JPEGOPTIM', '/usr/local/bin/jpegoptim' ); else define( 'JPEGOPTIM', false ); require dirname( __FILE__ ) . '/class-image-processor.php'; function httpdie( $code = '404 Not Found', $message = 'Error: 404 Not Found' ) { $numerical_error_code = preg_replace( '/[^\\d]/', '', $code ); do_action( 'bump_stats', "http_error-$numerical_error_code" ); header( 'HTTP/1.1 ' . $code ); die( $message ); } function fetch_raw_data( $url, $timeout = 10, $connect_timeout = 2 ) { $ch = curl_init( $url ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $connect_timeout ); curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout ); curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); curl_setopt( $ch, CURLOPT_MAXREDIRS, 3 ); curl_setopt( $ch, CURLOPT_USERAGENT, 'Photon/1.0' ); curl_setopt( $ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); curl_setopt( $ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); curl_setopt( $ch, CURLOPT_WRITEFUNCTION, function( $curl_handle, $data ) { global $raw_data, $raw_data_size, $remote_image_max_size; $data_size = strlen( $data ); $raw_data .= $data; $raw_data_size += $data_size; if ( $raw_data_size > $remote_image_max_size ) httpdie( '400 Bad Request', "You can only process images up to $remote_image_max_size bytes." ); return $data_size; } ); return curl_exec( $ch ); } $parsed = parse_url( $_SERVER['REQUEST_URI'] ); $exploded = explode( '/', $_SERVER['REQUEST_URI'] ); $origin_domain = strtolower( $exploded[1] ); $origin_domain_exception = array_key_exists( $origin_domain, $origin_domain_exceptions ) ? $origin_domain_exceptions[$origin_domain] : 0; $scheme = 'http' . ( array_key_exists( 'ssl', $_GET ) ? 's' : '' ) . '://'; parse_str( ( empty( $parsed['query'] ) ? '' : $parsed['query'] ), $_GET ); $ext = strtolower( pathinfo( $parsed['path'], PATHINFO_EXTENSION ) ); $url = $scheme . substr( $parsed['path'], 1 ); $url = preg_replace( '/#.*$/', '', $url ); $url = apply_filters( 'url', $url ); if ( isset( $_GET['q'] ) ) { if ( $origin_domain_exception & PHOTON__ALLOW_QUERY_STRINGS ) { $url .= '?' . preg_replace( '/#.*$/', '', (string) $_GET['q'] ); unset( $_GET['q'] ); } else { httpdie( '400 Bad Request', 'Sorry, the parameters you provided were not valid' ); } } if ( false === filter_var( $url, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED ) ) httpdie( '400 Bad Request', 'Sorry, the parameters you provided were not valid' ); $raw_data = ''; $raw_data_size = 0; $fetched = fetch_raw_data( $url ); if ( ! $fetched || empty( $raw_data ) ) httpdie( '504 Gateway Timeout', 'We cannot complete this request, remote data could not be fetched' ); foreach ( $disallowed_file_headers as $file_header ) { if ( substr( $raw_data, 0, strlen( $file_header ) ) == $file_header ) httpdie( '400 Bad Request', 'Error 0002. The type of image you are trying to process is not allowed.' ); } $img_proc = new Image_Processor(); if ( ! $img_proc ) httpdie( '500 Internal Server Error', 'Error 0003. Unable to load the image.' ); $img_proc->send_nosniff_header = true; $img_proc->norm_color_profile = false; $img_proc->send_bytes_saved = true; $img_proc->send_etag_header = true; $img_proc->canonical_url = $url; $img_proc->image_max_age = 63115200; $img_proc->image_data = $raw_data; if ( ! $img_proc->load_image() ) httpdie( '400 Bad Request', 'Error 0004. Unable to load the image.' ); if ( ! in_array( $img_proc->image_format, $allowed_types ) ) httpdie( '400 Bad Request', 'Error 0005. The type of image you are trying to process is not allowed.' ); $original_mime_type = $img_proc->mime_type; $img_proc->process_image(); // Update the stats of the processed functions foreach ( $img_proc->processed as $function_name ) { do_action( 'bump_stats', $function_name ); } switch ( $original_mime_type ) { case 'image/png': do_action( 'bump_stats', 'image_png' . ( 'image/webp' == $img_proc->mime_type ? '_as_webp' : '' ) ); do_action( 'bump_stats', 'png_bytes_saved', $img_proc->bytes_saved ); break; case 'image/gif': do_action( 'bump_stats', 'image_gif' ); break; default: do_action( 'bump_stats', 'image_jpeg' . ( 'image/webp' == $img_proc->mime_type ? '_as_webp' : '' ) ); do_action( 'bump_stats', 'jpg_bytes_saved', $img_proc->bytes_saved ); }