<?php
/**
 * Class A_Dynamic_Thumbnails_Storage_Driver
 * @mixin C_GalleryStorage_Driver_Base
 * @adapts I_GalleryStorage_Driver
 */
class A_Dynamic_Thumbnails_Storage_Driver extends Mixin
{
    function get_image_abspath($image, $size = FALSE, $check_existance = FALSE)
    {
        $retval = NULL;
        $dynthumbs = C_Dynamic_Thumbnails_Manager::get_instance();
        if ($dynthumbs && $dynthumbs->is_size_dynamic($size)) {
            // If we have the id, get the actual image entity
            if (is_numeric($image)) {
                $image = $this->object->_image_mapper->find($image);
            }
            // Ensure we have the image entity - user could have passed in an
            // incorrect id
            if (is_object($image)) {
                if ($folder_path = $this->object->get_cache_abspath($image->galleryid)) {
                    $params = $dynthumbs->get_params_from_name($size, true);
                    $image_filename = $dynthumbs->get_image_name($image, $params);
                    $image_path = implode(DIRECTORY_SEPARATOR, array(rtrim($folder_path, "\\/"), $image_filename));
                    if ($check_existance) {
                        if (@file_exists($image_path)) {
                            $retval = $image_path;
                        }
                    } else {
                        $retval = $image_path;
                    }
                }
            }
        } else {
            $retval = $this->call_parent('get_image_abspath', $image, $size, $check_existance);
        }
        return $retval;
    }
    function get_image_url($image, $size = 'full', $check_existance = FALSE)
    {
        $retval = NULL;
        $abspath = FALSE;
        $dynthumbs = C_Dynamic_Thumbnails_Manager::get_instance();
        if ($dynthumbs && $dynthumbs->is_size_dynamic($size)) {
            $abspath = $this->object->get_image_abspath($image, $size, true);
            // If abspath is NULL, then we assume we're dealing with a dynamic image
            if ($abspath == null) {
                $params = $dynthumbs->get_params_from_name($size, true);
                $retval = $dynthumbs->get_image_url($image, $params);
            } else {
                $check_existance = FALSE;
            }
        }
        if ($retval == null) {
            $retval = $this->call_parent('get_image_url', $image, $size, $check_existance, $abspath);
        }
        // Try generating the thumbnail
        if ($retval == null) {
            $params = array('watermark' => false, 'reflection' => false, 'crop' => true);
            $result = $this->generate_image_size($image, $size, $params);
            if ($result) {
                $retval = $this->call_parent('get_image_url', $image, $size, $check_existance);
            }
        }
        return $retval;
    }
    function get_image_dimensions($image, $size = 'full')
    {
        $retval = $this->call_parent('get_image_dimensions', $image, $size);
        if ($retval == null) {
            $dynthumbs = C_Dynamic_Thumbnails_Manager::get_instance();
            if ($dynthumbs && $dynthumbs->is_size_dynamic($size)) {
                $new_dims = $this->object->calculate_image_size_dimensions($image, $size);
                $retval = array('width' => $new_dims['real_width'], 'height' => $new_dims['real_height']);
            }
        }
        return $retval;
    }
    function get_image_size_params($image, $size, $params = null, $skip_defaults = false)
    {
        $dynthumbs = C_Dynamic_Thumbnails_Manager::get_instance();
        if ($dynthumbs && $dynthumbs->is_size_dynamic($size)) {
            $named_params = $dynthumbs->get_params_from_name($size, true);
            foreach ($named_params as $param_name => $param_value) {
                $params[$param_name] = $param_value;
            }
        }
        return $this->call_parent('get_image_size_params', $image, $size, $params, $skip_defaults);
    }
}
/**
 * Class C_Dynamic_Thumbnails_Controller
 * @implements I_Dynamic_Thumbnails_Controller
 */
class C_Dynamic_Thumbnails_Controller extends C_MVC_Controller
{
    static $_instances = array();
    function define($context = FALSE)
    {
        parent::define($context);
        $this->implement('I_Dynamic_Thumbnails_Controller');
    }
    /**
     * Returns an instance of this class
     *
     * @param string $context
     * @return C_Dynamic_Thumbnails_Controller
     */
    static function get_instance($context = FALSE)
    {
        if (!isset(self::$_instances[$context])) {
            $klass = get_class();
            self::$_instances[$context] = new $klass($context);
        }
        return self::$_instances[$context];
    }
    function index_action()
    {
        $dynthumbs = C_Dynamic_Thumbnails_Manager::get_instance();
        $uri = $_SERVER['REQUEST_URI'];
        $params = $dynthumbs->get_params_from_uri($uri);
        $request_params = $params;
        if ($params != null) {
            $storage = C_Gallery_Storage::get_instance();
            // Note, URLs should always include quality setting when returned by Gallery Storage component
            // this sanity check is mostly for manually testing URLs
            if (!isset($params['quality'])) {
                // Note: there's a problem when doing this as using the same set of parameters to *retrieve* the image path/URL will lead to a different filename than the one tha was used to *generate* it (which went through here)
                // The statement above about URLs always containing quality setting is not true anymore, this is because we need to retrieve default quality from the imgQuality and thumbquality settings, depending on "full" or "thumbnail" request in the ngglegacy storage
                //$params['quality'] = 100;
            }
            $image_id = $params['image'];
            $size = $dynthumbs->get_size_name($params);
            $abspath = $storage->get_image_abspath($image_id, $size, true);
            $valid = true;
            // Render invalid image if hash check fails
            if ($abspath == null) {
                $uri_plain = $dynthumbs->get_uri_from_params($request_params);
                $hash = wp_hash($uri_plain);
                if (strpos($uri, $hash) === false) {
                    $valid = false;
                    $filename = $this->object->find_static_file('invalid_image.png');
                    $this->set_content_type('image/png');
                    readfile($filename);
                    $this->render();
                }
            }
            if ($valid) {
                $storage->render_image($image_id, $size);
            }
        }
    }
}
class Mixin_Dynamic_Thumbnails_Manager extends Mixin
{
    function get_route_name()
    {
        return C_NextGen_Settings::get_instance()->get('dynamic_thumbnail_slug');
    }
    function _get_params_sanitized($params)
    {
        if (isset($params['rotation'])) {
            $rotation = intval($params['rotation']);
            if ($rotation && in_array(abs($rotation), array(90, 180, 270))) {
                $rotation = $rotation % 360;
                if ($rotation < 0) {
                    $rotation = 360 - $rotation;
                }
                $params['rotation'] = $rotation;
            } else {
                unset($params['rotation']);
            }
        }
        if (isset($params['flip'])) {
            $flip = strtolower($params['flip']);
            if (in_array($flip, array('h', 'v', 'hv'))) {
                $params['flip'] = $flip;
            } else {
                unset($params['flip']);
            }
        }
        return $params;
    }
    function get_uri_from_params($params)
    {
        $params = $this->object->_get_params_sanitized($params);
        $image = isset($params['image']) ? $params['image'] : null;
        $image_id = is_scalar($image) || is_null($image) ? (int) $image : $image->pid;
        $image_width = isset($params['width']) ? $params['width'] : null;
        $image_height = isset($params['height']) ? $params['height'] : null;
        $image_quality = isset($params['quality']) ? $params['quality'] : null;
        $image_type = isset($params['type']) ? $params['type'] : null;
        $image_crop = isset($params['crop']) ? $params['crop'] : null;
        $image_watermark = isset($params['watermark']) ? $params['watermark'] : null;
        $image_rotation = isset($params['rotation']) ? $params['rotation'] : null;
        $image_flip = isset($params['flip']) ? $params['flip'] : null;
        $image_reflection = isset($params['reflection']) ? $params['reflection'] : null;
        $uri = null;
        $uri .= '/';
        $uri .= $this->object->get_route_name() . '/';
        $uri .= strval($image_id) . '/';
        $uri .= strval($image_width) . 'x' . strval($image_height);
        if ($image_quality != null) {
            $uri .= 'x' . strval($image_quality);
        }
        $uri .= '/';
        if ($image_type != null) {
            $uri .= $image_type . '/';
        }
        if ($image_crop) {
            $uri .= 'crop/';
        }
        if ($image_watermark) {
            $uri .= 'watermark/';
        }
        if ($image_rotation) {
            $uri .= 'rotation-' . $image_rotation . '/';
        }
        if ($image_flip) {
            $uri .= 'flip-' . $image_flip . '/';
        }
        if ($image_reflection) {
            $uri .= 'reflection/';
        }
        return $uri;
    }
    function get_image_uri($image, $params)
    {
        $params['image'] = $image;
        $uri = $this->object->get_uri_from_params($params);
        if (substr($uri, -1) != '/') {
            $uri .= '/';
        }
        $uri .= wp_hash($uri) . '/';
        return $uri;
    }
    function get_image_url($image, $params)
    {
        return C_Router::get_instance()->get_url($this->object->get_image_uri($image, $params), FALSE, 'root');
    }
    function get_params_from_uri($uri)
    {
        $regex = '/\\/?' . $this->object->get_route_name() . '\\/(\\d+)(?:\\/(.*))?/';
        $match = null;
        // XXX move this URL clean up to I_Router?
        $uri = preg_replace('/\\/index.php\\//', '/', $uri, 1);
        $uri = trim($uri, '/');
        if (@preg_match($regex, $uri, $match) > 0) {
            $image_id = $match[1];
            $uri_args = isset($match[2]) ? explode('/', $match[2]) : array();
            $params = array('image' => $image_id);
            foreach ($uri_args as $uri_arg) {
                $uri_arg_set = explode('-', $uri_arg);
                $uri_arg_name = array_shift($uri_arg_set);
                $uri_arg_value = $uri_arg_set ? array_shift($uri_arg_set) : null;
                $size_match = null;
                if ($uri_arg == 'watermark') {
                    $params['watermark'] = true;
                } else {
                    if ($uri_arg_name == 'rotation') {
                        $params['rotation'] = $uri_arg_value;
                    } else {
                        if ($uri_arg_name == 'flip') {
                            $params['flip'] = $uri_arg_value;
                        } else {
                            if ($uri_arg == 'reflection') {
                                $params['reflection'] = true;
                            } else {
                                if ($uri_arg == 'crop') {
                                    $params['crop'] = true;
                                } else {
                                    if (in_array(strtolower($uri_arg), array('gif', 'jpg', 'png'))) {
                                        $params['type'] = $uri_arg;
                                    } else {
                                        if (preg_match('/(\\d+)x(\\d+)(?:x(\\d+))?/i', $uri_arg, $size_match) > 0) {
                                            $params['width'] = $size_match[1];
                                            $params['height'] = $size_match[2];
                                            if (isset($size_match[3])) {
                                                $params['quality'] = $size_match[3];
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return $this->object->_get_params_sanitized($params);
        }
        return null;
    }
    function _get_name_prefix_list()
    {
        return array('id' => 'nggid0', 'size' => 'ngg0dyn-', 'flags' => '00f0', 'flag' => array('w0' => 'watermark', 'c0' => 'crop', 'r1' => 'rotation', 'f1' => 'flip', 'r0' => 'reflection', 't0' => 'type'), 'flag_len' => 2, 'max_value_length' => 15);
    }
    function get_name_from_params($params, $only_size_name = false, $id_in_name = true)
    {
        $prefix_list = $this->object->_get_name_prefix_list();
        $id_prefix = $prefix_list['id'];
        $size_prefix = $prefix_list['size'];
        $flags_prefix = $prefix_list['flags'];
        $flags = $prefix_list['flag'];
        $max_value_length = $prefix_list['max_value_length'];
        $params = $this->object->_get_params_sanitized($params);
        $image = isset($params['image']) ? $params['image'] : null;
        $image_width = isset($params['width']) ? $params['width'] : null;
        $image_height = isset($params['height']) ? $params['height'] : null;
        $image_quality = isset($params['quality']) ? $params['quality'] : null;
        $extension = null;
        $name = null;
        // if $only_size_name is false then we include the file name and image id for the image
        if (!$only_size_name) {
            if (is_int($image)) {
                $imap = C_Image_Mapper::get_instance();
                $image = $imap->find($image);
            }
            if ($image != null) {
                // this is used to remove the extension and then add it back at the end of the name
                $extension = M_I18n::mb_pathinfo($image->filename, PATHINFO_EXTENSION);
                if ($extension != null) {
                    $extension = '.' . $extension;
                }
                $name .= M_I18n::mb_basename($image->filename, $extension);
                $name .= '-';
                if ($id_in_name) {
                    $image_id = strval($image->pid);
                    $id_len = min($max_value_length, strlen($image_id));
                    $id_len_hex = dechex($id_len);
                    // sanity check, should never occurr if $max_value_length is not messed up, ensure only 1 character is used to encode length or else skip parameter
                    if (strlen($id_len_hex) == 1) {
                        $name .= $id_prefix . $id_len . substr($image_id, 0, $id_len);
                        $name .= '-';
                    }
                }
            }
        }
        $name .= $size_prefix;
        $name .= strval($image_width) . 'x' . strval($image_height);
        if ($image_quality != null) {
            $name .= 'x' . $image_quality;
        }
        $name .= '-';
        $name .= $flags_prefix;
        foreach ($flags as $flag_prefix => $flag_name) {
            $flag_value = 0;
            if (isset($params[$flag_name])) {
                $flag_value = $params[$flag_name];
                if (!is_string($flag_value)) {
                    // only strings or ints allowed, sprintf is required because intval(0) returns '' and not '0'
                    $flag_value = intval($flag_value);
                    $flag_value = sprintf('%d', $flag_value);
                }
            }
            $flag_value = strval($flag_value);
            $flag_len = min($max_value_length, strlen($flag_value));
            $flag_len_hex = dechex($flag_len);
            // sanity check, should never occurr if $max_value_length is not messed up, ensure only 1 character is used to encode length or else skip parameter
            if (strlen($flag_len_hex) == 1) {
                $name .= $flag_prefix . $flag_len . substr($flag_value, 0, $flag_len);
            }
        }
        $name .= $extension;
        return $name;
    }
    function get_size_name($params)
    {
        $name = $this->object->get_name_from_params($params, true);
        return $name;
    }
    function get_image_name($image, $params)
    {
        $params['image'] = $image;
        $name = $this->object->get_name_from_params($params);
        return $name;
    }
    function get_params_from_name($name, $is_only_size_name = false)
    {
        $prefix_list = $this->object->_get_name_prefix_list();
        $id_prefix = $prefix_list['id'];
        $size_prefix = $prefix_list['size'];
        $flags_prefix = $prefix_list['flags'];
        $max_value_length = $prefix_list['max_value_length'];
        $size_name = null;
        $id_name = null;
        $params = array();
        if (!$is_only_size_name) {
            $extension = M_I18n::mb_pathinfo($name, PATHINFO_EXTENSION);
            if ($extension != null) {
                $extension = '.' . $extension;
            }
            $name = M_I18n::mb_basename($name, $extension);
        }
        $size_index = strrpos($name, $size_prefix);
        if ($size_index > 0 || $size_index === 0) {
            // check if name contains dynamic size/params info by looking for prefix
            $size_name = substr($name, $size_index);
        }
        if (!$is_only_size_name) {
            // name should contain the image id, search for prefix
            $id_index = strrpos($name, $id_prefix);
            if ($id_index > 0 || $id_index === 0) {
                if ($size_index > 0 && $size_index > $id_index) {
                    $id_name = substr($name, $id_index, $size_index - $id_index);
                } else {
                    $id_name = substr($name, $id_index);
                }
            }
        }
        // Double check we got a correct dynamic size/params string
        if (substr($size_name, 0, strlen($size_prefix)) == $size_prefix) {
            $flags = $prefix_list['flag'];
            // get the length of the flag id (the key in the $flags array) in the string (how many characters to consume)
            $flag_id_len = $prefix_list['flag_len'];
            $params_str = substr($size_name, strlen($size_prefix));
            $params_parts = explode('-', $params_str);
            // $param_part is a single param, separated by '-'
            foreach ($params_parts as $param_part) {
                // Parse WxHxQ - Q=quality
                $param_size = explode('x', $param_part);
                $param_size_count = count($param_size);
                if (substr($param_part, 0, strlen($flags_prefix)) == $flags_prefix) {
                    /* Set flags, using $flags keys as prefixes */
                    // move string pointer up (after the main flags prefix)
                    $param_flags = substr($param_part, strlen($flags_prefix));
                    $param_flags_len = strlen($param_flags);
                    $flags_todo = $flags;
                    while (true) {
                        // ensure we don't run into an infinite loop ;)
                        if (count($flags_todo) == 0 || strlen($param_flags) == 0) {
                            break;
                        }
                        // get the flag prefix (a key in the $flags array) using flag id length
                        $flag_prefix = substr($param_flags, 0, $flag_id_len);
                        // move string pointer up (after the single flag prefix)
                        $param_flags = substr($param_flags, $flag_id_len);
                        // get the length of the flag value in the string (how many characters to consume)
                        // flag value length is stored in a single hexadecimal character next to the flag prefix
                        $flag_value_len = min(hexdec(substr($param_flags, 0, 1)), min($max_value_length, strlen($param_flags) - 1));
                        // get the flag value
                        $flag_value = substr($param_flags, 1, $flag_value_len);
                        // move string pointer up (after the entire flag)
                        $param_flags = substr($param_flags, $flag_value_len + 1);
                        // make sure the flag is supported
                        if (isset($flags[$flag_prefix])) {
                            $flag_name = $flags[$flag_prefix];
                            if (is_numeric($flag_value)) {
                                // convert numerical flags to integers
                                $flag_value = intval($flag_value);
                            }
                            $params[$flag_name] = $flag_value;
                            if (isset($flags_todo[$flag_prefix])) {
                                unset($flags_todo[$flag_prefix]);
                            }
                        } else {
                            // XXX unknown flag?
                        }
                    }
                } else {
                    if ($param_size_count == 2 || $param_size_count == 3) {
                        // Set W H Q
                        $params['width'] = intval($param_size[0]);
                        $params['height'] = intval($param_size[1]);
                        if (isset($param_size[2]) && intval($param_size[2]) > 0) {
                            $params['quality'] = intval($param_size[2]);
                        }
                    }
                }
            }
        }
        // Double check we got a correct id string
        if (substr($id_name, 0, strlen($id_prefix)) == $id_prefix) {
            // move string pointer up (after the prefix)
            $id_name = substr($id_name, strlen($id_prefix));
            // get the length of the image id in the string (how many characters to consume)
            $id_len = min(hexdec(substr($id_name, 0, 1)), min($max_value_length, strlen($id_name) - 1));
            // get the id based on old position and id length
            $image_id = intval(substr($id_name, 1, $id_len));
            if ($image_id > 0) {
                $params['image'] = $image_id;
            }
        }
        return $this->object->_get_params_sanitized($params);
    }
    function is_size_dynamic($name, $is_only_size_name = false)
    {
        $params = $this->object->get_params_from_name($name, $is_only_size_name);
        if (isset($params['width']) && isset($params['height'])) {
            return true;
        }
        return false;
    }
}
/**
 * Class C_Dynamic_Thumbnails_Manager
 * @mixin Mixin_Dynamic_Thumbnails_Manager
 */
class C_Dynamic_Thumbnails_Manager extends C_Component
{
    static $_instances = array();
    function define($context = FALSE)
    {
        parent::define($context);
        $this->implement('I_Dynamic_Thumbnails_Manager');
        $this->add_mixin('Mixin_Dynamic_Thumbnails_Manager');
    }
    static function get_instance($context = False)
    {
        if (!isset(self::$_instances[$context])) {
            self::$_instances[$context] = new C_Dynamic_Thumbnails_Manager($context);
        }
        return self::$_instances[$context];
    }
}