summaryrefslogtreecommitdiff
path: root/system/libraries/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'system/libraries/drivers')
-rw-r--r--system/libraries/drivers/Cache.php42
-rw-r--r--system/libraries/drivers/Cache/File.php255
-rw-r--r--system/libraries/drivers/Cache/Memcache.php132
-rw-r--r--system/libraries/drivers/Cache/Xcache.php161
-rw-r--r--system/libraries/drivers/Config.php257
-rw-r--r--system/libraries/drivers/Config/Array.php83
-rw-r--r--system/libraries/drivers/Image.php158
-rw-r--r--system/libraries/drivers/Image/GD.php440
-rw-r--r--system/libraries/drivers/Image/GraphicsMagick.php225
-rw-r--r--system/libraries/drivers/Image/ImageMagick.php233
-rw-r--r--system/libraries/drivers/Log.php22
-rw-r--r--system/libraries/drivers/Log/Database.php40
-rw-r--r--system/libraries/drivers/Log/File.php44
-rw-r--r--system/libraries/drivers/Log/Syslog.php34
-rw-r--r--system/libraries/drivers/Session.php70
-rw-r--r--system/libraries/drivers/Session/Cache.php108
-rw-r--r--system/libraries/drivers/Session/Cookie.php83
-rw-r--r--system/libraries/drivers/Session/Database.php178
18 files changed, 2565 insertions, 0 deletions
diff --git a/system/libraries/drivers/Cache.php b/system/libraries/drivers/Cache.php
new file mode 100644
index 0000000..9741509
--- /dev/null
+++ b/system/libraries/drivers/Cache.php
@@ -0,0 +1,42 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Cache driver abstract class.
+ *
+ * $Id$
+ *
+ * @package Cache
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+abstract class Cache_Driver {
+ /**
+ * Set cache items
+ */
+ abstract public function set($items, $tags = NULL, $lifetime = NULL);
+
+ /**
+ * Get a cache items by key
+ */
+ abstract public function get($keys, $single = FALSE);
+
+ /**
+ * Get cache items by tag
+ */
+ abstract public function get_tag($tags);
+
+ /**
+ * Delete cache item by key
+ */
+ abstract public function delete($keys);
+
+ /**
+ * Delete cache items by tag
+ */
+ abstract public function delete_tag($tags);
+
+ /**
+ * Empty the cache
+ */
+ abstract public function delete_all();
+} // End Cache Driver \ No newline at end of file
diff --git a/system/libraries/drivers/Cache/File.php b/system/libraries/drivers/Cache/File.php
new file mode 100644
index 0000000..d6ec037
--- /dev/null
+++ b/system/libraries/drivers/Cache/File.php
@@ -0,0 +1,255 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Memcache-based Cache driver.
+ *
+ * $Id: File.php 4605 2009-09-14 17:22:21Z kiall $
+ *
+ * @package Cache
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Cache_File_Driver extends Cache_Driver {
+ protected $config;
+ protected $backend;
+
+ public function __construct($config)
+ {
+ $this->config = $config;
+ $this->config['directory'] = str_replace('\\', '/', realpath($this->config['directory'])).'/';
+
+ if ( ! is_dir($this->config['directory']) OR ! is_writable($this->config['directory']))
+ throw new Cache_Exception('The configured cache directory, :directory:, is not writable.', array(':directory:' => $this->config['directory']));
+ }
+
+ /**
+ * Finds an array of files matching the given id or tag.
+ *
+ * @param string cache key or tag
+ * @param bool search for tags
+ * @return array of filenames matching the id or tag
+ */
+ public function exists($keys, $tag = FALSE)
+ {
+ if ($keys === TRUE)
+ {
+ // Find all the files
+ return glob($this->config['directory'].'*~*~*');
+ }
+ elseif ($tag === TRUE)
+ {
+ // Find all the files that have the tag name
+ $paths = array();
+
+ foreach ( (array) $keys as $tag)
+ {
+ $paths = array_merge($paths, glob($this->config['directory'].'*~*'.$tag.'*~*'));
+ }
+
+ // Find all tags matching the given tag
+ $files = array();
+
+ foreach ($paths as $path)
+ {
+ // Split the files
+ $tags = explode('~', basename($path));
+
+ // Find valid tags
+ if (count($tags) !== 3 OR empty($tags[1]))
+ continue;
+
+ // Split the tags by plus signs, used to separate tags
+ $item_tags = explode('+', $tags[1]);
+
+ // Check each supplied tag, and match aginst the tags on each item.
+ foreach ($keys as $tag)
+ {
+ if (in_array($tag, $item_tags))
+ {
+ // Add the file to the array, it has the requested tag
+ $files[] = $path;
+ }
+ }
+ }
+
+ return $files;
+ }
+ else
+ {
+ $paths = array();
+
+ foreach ( (array) $keys as $key)
+ {
+ // Find the file matching the given key
+ $paths = array_merge($paths, glob($this->config['directory'].str_replace(array('/', '\\', ' '), '_', $key).'~*'));
+ }
+
+ return $paths;
+ }
+ }
+
+ public function set($items, $tags = NULL, $lifetime = NULL)
+ {
+ if ($lifetime !== 0)
+ {
+ // File driver expects unix timestamp
+ $lifetime += time();
+ }
+
+
+ if ( ! is_null($tags) AND ! empty($tags))
+ {
+ // Convert the tags into a string list
+ $tags = implode('+', (array) $tags);
+ }
+
+ $success = TRUE;
+
+ foreach ($items as $key => $value)
+ {
+ if (is_resource($value))
+ throw new Cache_Exception('Caching of resources is impossible, because resources cannot be serialised.');
+
+ // Remove old cache file
+ $this->delete($key);
+
+ if ( ! (bool) file_put_contents($this->config['directory'].str_replace(array('/', '\\', ' '), '_', $key).'~'.$tags.'~'.$lifetime, serialize($value)))
+ {
+ $success = FALSE;
+ }
+ }
+
+ return $success;
+ }
+
+ public function get($keys, $single = FALSE)
+ {
+ $items = array();
+
+ if ($files = $this->exists($keys))
+ {
+ // Turn off errors while reading the files
+ $ER = error_reporting(0);
+
+ foreach ($files as $file)
+ {
+ // Validate that the item has not expired
+ if ($this->expired($file))
+ continue;
+
+ list($key, $junk) = explode('~', basename($file), 2);
+
+ if (($data = file_get_contents($file)) !== FALSE)
+ {
+ // Unserialize the data
+ $data = unserialize($data);
+ }
+ else
+ {
+ $data = NULL;
+ }
+
+ $items[$key] = $data;
+ }
+
+ // Turn errors back on
+ error_reporting($ER);
+ }
+
+ // Reutrn a single item if only one key was requested
+ if ($single)
+ {
+ return (count($items) > 0) ? current($items) : NULL;
+ }
+ else
+ {
+ return $items;
+ }
+ }
+
+ /**
+ * Get cache items by tag
+ */
+ public function get_tag($tags)
+ {
+ // An array will always be returned
+ $result = array();
+
+ if ($paths = $this->exists($tags, TRUE))
+ {
+ // Find all the files with the given tag
+ foreach ($paths as $path)
+ {
+ // Get the id from the filename
+ list($key, $junk) = explode('~', basename($path), 2);
+
+ if (($data = $this->get($key, TRUE)) !== FALSE)
+ {
+ // Add the result to the array
+ $result[$key] = $data;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Delete cache items by keys or tags
+ */
+ public function delete($keys, $tag = FALSE)
+ {
+ $success = TRUE;
+
+ $paths = $this->exists($keys, $tag);
+
+ // Disable all error reporting while deleting
+ $ER = error_reporting(0);
+
+ foreach ($paths as $path)
+ {
+ // Remove the cache file
+ if ( ! unlink($path))
+ {
+ Kohana_Log::add('error', 'Cache: Unable to delete cache file: '.$path);
+ $success = FALSE;
+ }
+ }
+
+ // Turn on error reporting again
+ error_reporting($ER);
+
+ return $success;
+ }
+
+ /**
+ * Delete cache items by tag
+ */
+ public function delete_tag($tags)
+ {
+ return $this->delete($tags, TRUE);
+ }
+
+ /**
+ * Empty the cache
+ */
+ public function delete_all()
+ {
+ return $this->delete(TRUE);
+ }
+
+ /**
+ * Check if a cache file has expired by filename.
+ *
+ * @param string|array array of filenames
+ * @return bool
+ */
+ protected function expired($file)
+ {
+ // Get the expiration time
+ $expires = (int) substr($file, strrpos($file, '~') + 1);
+
+ // Expirations of 0 are "never expire"
+ return ($expires !== 0 AND $expires <= time());
+ }
+} // End Cache Memcache Driver
diff --git a/system/libraries/drivers/Cache/Memcache.php b/system/libraries/drivers/Cache/Memcache.php
new file mode 100644
index 0000000..13d61d8
--- /dev/null
+++ b/system/libraries/drivers/Cache/Memcache.php
@@ -0,0 +1,132 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Memcache-based Cache driver.
+ *
+ * $Id$
+ *
+ * @package Cache
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Cache_Memcache_Driver extends Cache_Driver {
+ protected $config;
+ protected $backend;
+ protected $flags;
+
+ public function __construct($config)
+ {
+ if ( ! extension_loaded('memcache'))
+ throw new Cache_Exception('The memcache PHP extension must be loaded to use this driver.');
+
+ ini_set('memcache.allow_failover', (isset($config['allow_failover']) AND $config['allow_failover']) ? TRUE : FALSE);
+
+ $this->config = $config;
+ $this->backend = new Memcache;
+
+ $this->flags = (isset($config['compression']) AND $config['compression']) ? MEMCACHE_COMPRESSED : FALSE;
+
+ foreach ($config['servers'] as $server)
+ {
+ // Make sure all required keys are set
+ $server += array('host' => '127.0.0.1',
+ 'port' => 11211,
+ 'persistent' => FALSE,
+ 'weight' => 1,
+ 'timeout' => 1,
+ 'retry_interval' => 15
+ );
+
+ // Add the server to the pool
+ $this->backend->addServer($server['host'], $server['port'], (bool) $server['persistent'], (int) $server['weight'], (int) $server['timeout'], (int) $server['retry_interval'], TRUE, array($this,'_memcache_failure_callback'));
+ }
+ }
+
+ public function _memcache_failure_callback($host, $port)
+ {
+ $this->backend->setServerParams($host, $port, 1, -1, FALSE);
+ Kohana_Log::add('error', __('Cache: Memcache server down: :host:::port:',array(':host:' => $host,':port:' => $port)));
+ }
+
+ public function set($items, $tags = NULL, $lifetime = NULL)
+ {
+ if ($lifetime !== 0)
+ {
+ // Memcache driver expects unix timestamp
+ $lifetime += time();
+ }
+
+ if ($tags !== NULL)
+ throw new Cache_Exception('Memcache driver does not support tags');
+
+ foreach ($items as $key => $value)
+ {
+ if (is_resource($value))
+ throw new Cache_Exception('Caching of resources is impossible, because resources cannot be serialised.');
+
+ if ( ! $this->backend->set($key, $value, $this->flags, $lifetime))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ public function get($keys, $single = FALSE)
+ {
+ $items = $this->backend->get($keys);
+
+ if ($single)
+ {
+ if ($items === FALSE)
+ return NULL;
+
+ return (count($items) > 0) ? current($items) : NULL;
+ }
+ else
+ {
+ return ($items === FALSE) ? array() : $items;
+ }
+ }
+
+ /**
+ * Get cache items by tag
+ */
+ public function get_tag($tags)
+ {
+ throw new Cache_Exception('Memcache driver does not support tags');
+ }
+
+ /**
+ * Delete cache item by key
+ */
+ public function delete($keys)
+ {
+ foreach ($keys as $key)
+ {
+ if ( ! $this->backend->delete($key))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Delete cache items by tag
+ */
+ public function delete_tag($tags)
+ {
+ throw new Cache_Exception('Memcache driver does not support tags');
+ }
+
+ /**
+ * Empty the cache
+ */
+ public function delete_all()
+ {
+ return $this->backend->flush();
+ }
+} // End Cache Memcache Driver
diff --git a/system/libraries/drivers/Cache/Xcache.php b/system/libraries/drivers/Cache/Xcache.php
new file mode 100644
index 0000000..6761983
--- /dev/null
+++ b/system/libraries/drivers/Cache/Xcache.php
@@ -0,0 +1,161 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * XCache-based Cache driver.
+ *
+ * $Id: Memcache.php 4605 2009-09-14 17:22:21Z kiall $
+ *
+ * @package Cache
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ * @TODO Check if XCache cleans its own keys.
+ */
+class Cache_Xcache_Driver extends Cache_Driver {
+ protected $config;
+
+ public function __construct($config)
+ {
+ if ( ! extension_loaded('xcache'))
+ throw new Cache_Exception('The xcache PHP extension must be loaded to use this driver.');
+
+ $this->config = $config;
+ }
+
+ public function set($items, $tags = NULL, $lifetime = NULL)
+ {
+ if ($tags !== NULL)
+ {
+ Kohana_Log::add('debug', __('Cache: XCache driver does not support tags'));
+ }
+
+ foreach ($items as $key => $value)
+ {
+ if (is_resource($value))
+ throw new Cache_Exception('Caching of resources is impossible, because resources cannot be serialised.');
+
+ if ( ! xcache_set($key, $value, $lifetime))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ public function get($keys, $single = FALSE)
+ {
+ $items = array();
+
+ foreach ($keys as $key)
+ {
+ if (xcache_isset($key))
+ {
+ $items[$key] = xcache_get($key);
+ }
+ else
+ {
+ $items[$key] = NULL;
+ }
+ }
+
+ if ($single)
+ {
+ return ($items === FALSE OR count($items) > 0) ? current($items) : NULL;
+ }
+ else
+ {
+ return ($items === FALSE) ? array() : $items;
+ }
+ }
+
+ /**
+ * Get cache items by tag
+ */
+ public function get_tag($tags)
+ {
+ Kohana_Log::add('debug', __('Cache: XCache driver does not support tags'));
+ return NULL;
+ }
+
+ /**
+ * Delete cache item by key
+ */
+ public function delete($keys)
+ {
+ foreach ($keys as $key)
+ {
+ if ( ! xcache_unset($key))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Delete cache items by tag
+ */
+ public function delete_tag($tags)
+ {
+ Kohana_Log::add('debug', __('Cache: XCache driver does not support tags'));
+ return NULL;
+ }
+
+ /**
+ * Empty the cache
+ */
+ public function delete_all()
+ {
+ $this->auth();
+ $result = TRUE;
+
+ for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++)
+ {
+ if (xcache_clear_cache(XC_TYPE_VAR, $i) !== NULL)
+ {
+ $result = FALSE;
+ break;
+ }
+ }
+
+ // Undo the login
+ $this->auth(TRUE);
+
+ return $result;
+ }
+
+ private function auth($reverse = FALSE)
+ {
+ static $backup = array();
+
+ $keys = array('PHP_AUTH_USER', 'PHP_AUTH_PW');
+
+ foreach ($keys as $key)
+ {
+ if ($reverse)
+ {
+ if (isset($backup[$key]))
+ {
+ $_SERVER[$key] = $backup[$key];
+ unset($backup[$key]);
+ }
+ else
+ {
+ unset($_SERVER[$key]);
+ }
+ }
+ else
+ {
+ $value = getenv($key);
+
+ if ( ! empty($value))
+ {
+ $backup[$key] = $value;
+ }
+
+ $_SERVER[$key] = $this->config->{$key};
+ }
+ }
+ }
+} // End Cache XCache Driver
diff --git a/system/libraries/drivers/Config.php b/system/libraries/drivers/Config.php
new file mode 100644
index 0000000..a82684b
--- /dev/null
+++ b/system/libraries/drivers/Config.php
@@ -0,0 +1,257 @@
+<?php defined('SYSPATH') or die('No direct script access.');
+/**
+ * Kohana_Config abstract driver to get and set
+ * configuration options.
+ *
+ * Specific drivers should implement caching and encryption
+ * as they deem appropriate.
+ *
+ * $Id: Config.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Kohana_Config
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ * @abstract
+ */
+abstract class Config_Driver {
+
+ /**
+ * Internal caching
+ *
+ * @var Cache
+ */
+ protected $cache;
+
+ /**
+ * The name of the internal cache
+ *
+ * @var string
+ */
+ protected $cache_name = 'Kohana_Config_Cache';
+
+ /**
+ * Cache Lifetime
+ *
+ * @var mixed
+ */
+ protected $cache_lifetime = FALSE;
+
+ /**
+ * The Encryption library
+ *
+ * @var Encrypt
+ */
+ protected $encrypt;
+
+ /**
+ * The config loaded
+ *
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * The changed status of configuration values,
+ * current state versus the stored state.
+ *
+ * @var bool
+ */
+ protected $changed = FALSE;
+
+ /**
+ * Determines if any config has been loaded yet
+ */
+ public $loaded = FALSE;
+
+ /**
+ * Array driver constructor. Sets up the PHP array
+ * driver, including caching and encryption if
+ * required
+ *
+ * @access public
+ */
+ public function __construct($config)
+ {
+
+ if (($cache_setting = $config['internal_cache']) !== FALSE)
+ {
+ $this->cache_lifetime = $cache_setting;
+ // Restore the cached configuration
+ $this->config = $this->load_cache();
+
+ if (count($this->config) > 0)
+ $this->loaded = TRUE;
+
+ // Add the save cache method to system.shutshut event
+ Event::add('system.shutdown', array($this, 'save_cache'));
+ }
+
+ }
+
+ /**
+ * Gets a value from config. If required is TRUE
+ * then get will throw an exception if value cannot
+ * be loaded.
+ *
+ * @param string key the setting to get
+ * @param bool slash remove trailing slashes
+ * @param bool required is setting required?
+ * @return mixed
+ * @access public
+ */
+ public function get($key, $slash = FALSE, $required = FALSE)
+ {
+ // Get the group name from the key
+ $group = explode('.', $key, 2);
+ $group = $group[0];
+
+ // Check for existing value and load it dynamically if required
+ if ( ! isset($this->config[$group]))
+ $this->config[$group] = $this->load($group, $required);
+
+ // Get the value of the key string
+ $value = Kohana::key_string($this->config, $key);
+
+ if ($slash === TRUE AND is_string($value) AND $value !== '')
+ {
+ // Force the value to end with "/"
+ $value = rtrim($value, '/').'/';
+ }
+
+ if (($required === TRUE) AND ($value === null))
+ throw new Kohana_Config_Exception('Value not found in config driver');
+
+ $this->loaded = TRUE;
+ return $value;
+ }
+
+ /**
+ * Sets a new value to the configuration
+ *
+ * @param string key
+ * @param mixed value
+ * @return bool
+ * @access public
+ */
+ public function set($key, $value)
+ {
+ // Do this to make sure that the config array is already loaded
+ $this->get($key);
+
+ if (substr($key, 0, 7) === 'routes.')
+ {
+ // Routes cannot contain sub keys due to possible dots in regex
+ $keys = explode('.', $key, 2);
+ }
+ else
+ {
+ // Convert dot-noted key string to an array
+ $keys = explode('.', $key);
+ }
+
+ // Used for recursion
+ $conf =& $this->config;
+ $last = count($keys) - 1;
+
+ foreach ($keys as $i => $k)
+ {
+ if ($i === $last)
+ {
+ $conf[$k] = $value;
+ }
+ else
+ {
+ $conf =& $conf[$k];
+ }
+ }
+
+ if (substr($key,0,12) === 'core.modules')
+ {
+ // Reprocess the include paths
+ Kohana::include_paths(TRUE);
+ }
+
+ // Set config to changed
+ return $this->changed = TRUE;
+ }
+
+ /**
+ * Clear the configuration
+ *
+ * @param string group
+ * @return bool
+ * @access public
+ */
+ public function clear($group)
+ {
+ // Remove the group from config
+ unset($this->config[$group]);
+
+ // Set config to changed
+ return $this->changed = TRUE;
+ }
+
+ /**
+ * Checks whether the setting exists in
+ * config
+ *
+ * @param string $key
+ * @return bool
+ * @access public
+ */
+ public function setting_exists($key)
+ {
+ return $this->get($key) === NULL;
+ }
+
+ /**
+ * Loads a configuration group based on the setting
+ *
+ * @param string group
+ * @param bool required
+ * @return array
+ * @access public
+ * @abstract
+ */
+ abstract public function load($group, $required = FALSE);
+
+ /**
+ * Loads the cached version of this configuration driver
+ *
+ * @return array
+ * @access public
+ */
+ public function load_cache()
+ {
+ // Load the cache for this configuration
+ $cached_config = Kohana::cache($this->cache_name, $this->cache_lifetime);
+
+ // If the configuration wasn't loaded from the cache
+ if ($cached_config === NULL)
+ $cached_config = array();
+
+ // Return the cached config
+ return $cached_config;
+ }
+
+ /**
+ * Saves a cached version of this configuration driver
+ *
+ * @return bool
+ * @access public
+ */
+ public function save_cache()
+ {
+ // If this configuration has changed
+ if ($this->get('core.internal_cache') !== FALSE AND $this->changed)
+ {
+ $data = $this->config;
+
+ // Save the cache
+ return Kohana::cache_save($this->cache_name, $data, $this->cache_lifetime);
+ }
+
+ return TRUE;
+ }
+} // End Kohana_Config_Driver \ No newline at end of file
diff --git a/system/libraries/drivers/Config/Array.php b/system/libraries/drivers/Config/Array.php
new file mode 100644
index 0000000..b2ca19b
--- /dev/null
+++ b/system/libraries/drivers/Config/Array.php
@@ -0,0 +1,83 @@
+<?php defined('SYSPATH') or die('No direct script access.');
+/**
+ * Kohana_Config Array driver to get and set
+ * configuration options using PHP arrays.
+ *
+ * This driver can cache and encrypt settings
+ * if required.
+ *
+ * $Id: Array.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Kohana_Config
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Config_Array_Driver extends Config_Driver {
+
+ /**
+ * Internal caching
+ *
+ * @var Cache
+ */
+ protected $cache;
+
+ /**
+ * The name of the internal cache
+ *
+ * @var string
+ */
+ protected $cache_name = 'Kohana_Config_Array_Cache';
+
+ /**
+ * The Encryption library
+ *
+ * @var Encrypt
+ */
+ protected $encrypt;
+
+ /**
+ * Loads a configuration group based on the setting
+ *
+ * @param string group
+ * @param bool required
+ * @return array
+ * @access public
+ */
+ public function load($group, $required = FALSE)
+ {
+ if ($group === 'core')
+ {
+ // Load the application configuration file
+ require APPPATH.'config/config'.EXT;
+
+ if ( ! isset($config['site_domain']))
+ {
+ // Invalid config file
+ throw new Kohana_Config_Exception('Your Kohana application configuration file is not valid.');
+ }
+
+ return $config;
+ }
+
+ // Load matching configs
+ $configuration = array();
+
+ if ($files = Kohana::find_file('config', $group, $required))
+ {
+ foreach ($files as $file)
+ {
+ require $file;
+
+ if (isset($config) AND is_array($config))
+ {
+ // Merge in configuration
+ $configuration = array_merge($configuration, $config);
+ }
+ }
+ }
+
+ // Return merged configuration
+ return $configuration;
+ }
+} // End Config_Array_Driver \ No newline at end of file
diff --git a/system/libraries/drivers/Image.php b/system/libraries/drivers/Image.php
new file mode 100644
index 0000000..39936c3
--- /dev/null
+++ b/system/libraries/drivers/Image.php
@@ -0,0 +1,158 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Image API driver.
+ *
+ * $Id: Image.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Image
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+abstract class Image_Driver {
+
+ // Reference to the current image
+ protected $image;
+
+ // Reference to the temporary processing image
+ protected $tmp_image;
+
+ // Processing errors
+ protected $errors = array();
+
+ /**
+ * Executes a set of actions, defined in pairs.
+ *
+ * @param array actions
+ * @return boolean
+ */
+ public function execute($actions)
+ {
+ foreach ($actions as $func => $args)
+ {
+ if ( ! $this->$func($args))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Sanitize and normalize a geometry array based on the temporary image
+ * width and height. Valid properties are: width, height, top, left.
+ *
+ * @param array geometry properties
+ * @return void
+ */
+ protected function sanitize_geometry( & $geometry)
+ {
+ list($width, $height) = $this->properties();
+
+ // Turn off error reporting
+ $reporting = error_reporting(0);
+
+ // Width and height cannot exceed current image size
+ $geometry['width'] = min($geometry['width'], $width);
+ $geometry['height'] = min($geometry['height'], $height);
+
+ // Set standard coordinates if given, otherwise use pixel values
+ if ($geometry['top'] === 'center')
+ {
+ $geometry['top'] = floor(($height / 2) - ($geometry['height'] / 2));
+ }
+ elseif ($geometry['top'] === 'top')
+ {
+ $geometry['top'] = 0;
+ }
+ elseif ($geometry['top'] === 'bottom')
+ {
+ $geometry['top'] = $height - $geometry['height'];
+ }
+
+ // Set standard coordinates if given, otherwise use pixel values
+ if ($geometry['left'] === 'center')
+ {
+ $geometry['left'] = floor(($width / 2) - ($geometry['width'] / 2));
+ }
+ elseif ($geometry['left'] === 'left')
+ {
+ $geometry['left'] = 0;
+ }
+ elseif ($geometry['left'] === 'right')
+ {
+ $geometry['left'] = $width - $geometry['height'];
+ }
+
+ // Restore error reporting
+ error_reporting($reporting);
+ }
+
+ /**
+ * Return the current width and height of the temporary image. This is mainly
+ * needed for sanitizing the geometry.
+ *
+ * @return array width, height
+ */
+ abstract protected function properties();
+
+ /**
+ * Process an image with a set of actions.
+ *
+ * @param string image filename
+ * @param array actions to execute
+ * @param string destination directory path
+ * @param string destination filename
+ * @param boolean render the image
+ * @param string background color
+ * @return boolean
+ */
+ abstract public function process($image, $actions, $dir, $file, $render = FALSE, $background = NULL);
+
+ /**
+ * Flip an image. Valid directions are horizontal and vertical.
+ *
+ * @param integer direction to flip
+ * @return boolean
+ */
+ abstract function flip($direction);
+
+ /**
+ * Crop an image. Valid properties are: width, height, top, left.
+ *
+ * @param array new properties
+ * @return boolean
+ */
+ abstract function crop($properties);
+
+ /**
+ * Resize an image. Valid properties are: width, height, and master.
+ *
+ * @param array new properties
+ * @return boolean
+ */
+ abstract public function resize($properties);
+
+ /**
+ * Rotate an image. Valid amounts are -180 to 180.
+ *
+ * @param integer amount to rotate
+ * @return boolean
+ */
+ abstract public function rotate($amount);
+
+ /**
+ * Sharpen and image. Valid amounts are 1 to 100.
+ *
+ * @param integer amount to sharpen
+ * @return boolean
+ */
+ abstract public function sharpen($amount);
+
+ /**
+ * Overlay a second image. Valid properties are: overlay_file, mime, x, y and transparency.
+ *
+ * @return boolean
+ */
+ abstract public function composite($properties);
+
+} // End Image Driver \ No newline at end of file
diff --git a/system/libraries/drivers/Image/GD.php b/system/libraries/drivers/Image/GD.php
new file mode 100644
index 0000000..6ffffe8
--- /dev/null
+++ b/system/libraries/drivers/Image/GD.php
@@ -0,0 +1,440 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * GD Image Driver.
+ *
+ * $Id: GD.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Image
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Image_GD_Driver extends Image_Driver {
+
+ // A transparent PNG as a string
+ protected static $blank_png;
+ protected static $blank_png_width;
+ protected static $blank_png_height;
+
+ public function __construct()
+ {
+ // Make sure that GD2 is available
+ if ( ! function_exists('gd_info'))
+ throw new Kohana_Exception('The Image library requires GD2. Please see http://php.net/gd_info for more information.');
+
+ // Get the GD information
+ $info = gd_info();
+
+ // Make sure that the GD2 is installed
+ if (strpos($info['GD Version'], '2.') === FALSE)
+ throw new Kohana_Exception('The Image library requires GD2. Please see http://php.net/gd_info for more information.');
+ }
+
+ public function process($image, $actions, $dir, $file, $render = FALSE, $background = NULL)
+ {
+ // Set the "create" function
+ switch ($image['type'])
+ {
+ case IMAGETYPE_JPEG:
+ $create = 'imagecreatefromjpeg';
+ break;
+ case IMAGETYPE_GIF:
+ $create = 'imagecreatefromgif';
+ break;
+ case IMAGETYPE_PNG:
+ $create = 'imagecreatefrompng';
+ break;
+ }
+
+ // Set the "save" function
+ switch (strtolower(substr(strrchr($file, '.'), 1)))
+ {
+ case 'jpg':
+ case 'jpeg':
+ $save = 'imagejpeg';
+ break;
+ case 'gif':
+ $save = 'imagegif';
+ break;
+ case 'png':
+ $save = 'imagepng';
+ break;
+ }
+
+ // Make sure the image type is supported for import
+ if (empty($create) OR ! function_exists($create))
+ throw new Kohana_Exception('The specified image, :type:, is not an allowed image type.', array(':type:' => $image['file']));
+
+ // Make sure the image type is supported for saving
+ if (empty($save) OR ! function_exists($save))
+ throw new Kohana_Exception('The specified image, :type:, is not an allowed image type.', array(':type:' => $dir.$file));
+
+ // Load the image
+ $this->image = $image;
+
+ // Create the GD image resource
+ $this->tmp_image = $create($image['file']);
+
+ // Get the quality setting from the actions
+ $quality = arr::remove('quality', $actions);
+
+ if ($status = $this->execute($actions))
+ {
+ // Prevent the alpha from being lost
+ imagealphablending($this->tmp_image, TRUE);
+ imagesavealpha($this->tmp_image, TRUE);
+
+ switch ($save)
+ {
+ case 'imagejpeg':
+ // Default the quality to 95
+ ($quality === NULL) and $quality = 95;
+ break;
+ case 'imagegif':
+ // Remove the quality setting, GIF doesn't use it
+ unset($quality);
+ break;
+ case 'imagepng':
+ // Always use a compression level of 9 for PNGs. This does not
+ // affect quality, it only increases the level of compression!
+ $quality = 9;
+ break;
+ }
+
+ if ($render === FALSE)
+ {
+ // Set the status to the save return value, saving with the quality requested
+ $status = isset($quality) ? $save($this->tmp_image, $dir.$file, $quality) : $save($this->tmp_image, $dir.$file);
+ }
+ else
+ {
+ // Output the image directly to the browser
+ switch ($save)
+ {
+ case 'imagejpeg':
+ header('Content-Type: image/jpeg');
+ break;
+ case 'imagegif':
+ header('Content-Type: image/gif');
+ break;
+ case 'imagepng':
+ header('Content-Type: image/png');
+ break;
+ }
+
+ $status = isset($quality) ? $save($this->tmp_image, NULL, $quality) : $save($this->tmp_image);
+ }
+
+ // Destroy the temporary image
+ imagedestroy($this->tmp_image);
+ }
+
+ return $status;
+ }
+
+ public function flip($direction)
+ {
+ // Get the current width and height
+ $width = imagesx($this->tmp_image);
+ $height = imagesy($this->tmp_image);
+
+ // Create the flipped image
+ $flipped = $this->imagecreatetransparent($width, $height);
+
+ if ($direction === Image::HORIZONTAL)
+ {
+ for ($x = 0; $x < $width; $x++)
+ {
+ $status = imagecopy($flipped, $this->tmp_image, $x, 0, $width - $x - 1, 0, 1, $height);
+ }
+ }
+ elseif ($direction === Image::VERTICAL)
+ {
+ for ($y = 0; $y < $height; $y++)
+ {
+ $status = imagecopy($flipped, $this->tmp_image, 0, $y, 0, $height - $y - 1, $width, 1);
+ }
+ }
+ else
+ {
+ // Do nothing
+ return TRUE;
+ }
+
+ if ($status === TRUE)
+ {
+ // Swap the new image for the old one
+ imagedestroy($this->tmp_image);
+ $this->tmp_image = $flipped;
+ }
+
+ return $status;
+ }
+
+ public function crop($properties)
+ {
+ // Sanitize the cropping settings
+ $this->sanitize_geometry($properties);
+
+ // Get the current width and height
+ $width = imagesx($this->tmp_image);
+ $height = imagesy($this->tmp_image);
+
+ // Create the temporary image to copy to
+ $img = $this->imagecreatetransparent($properties['width'], $properties['height']);
+
+ // Execute the crop
+ if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, $properties['left'], $properties['top'], $width, $height, $width, $height))
+ {
+ // Swap the new image for the old one
+ imagedestroy($this->tmp_image);
+ $this->tmp_image = $img;
+ }
+
+ return $status;
+ }
+
+ public function resize($properties)
+ {
+ // Get the current width and height
+ $width = imagesx($this->tmp_image);
+ $height = imagesy($this->tmp_image);
+
+ if (substr($properties['width'], -1) === '%')
+ {
+ // Recalculate the percentage to a pixel size
+ $properties['width'] = round($width * (substr($properties['width'], 0, -1) / 100));
+ }
+
+ if (substr($properties['height'], -1) === '%')
+ {
+ // Recalculate the percentage to a pixel size
+ $properties['height'] = round($height * (substr($properties['height'], 0, -1) / 100));
+ }
+
+ // Recalculate the width and height, if they are missing
+ empty($properties['width']) and $properties['width'] = round($width * $properties['height'] / $height);
+ empty($properties['height']) and $properties['height'] = round($height * $properties['width'] / $width);
+
+ if ($properties['master'] === Image::AUTO)
+ {
+ // Change an automatic master dim to the correct type
+ $properties['master'] = (($width / $properties['width']) > ($height / $properties['height'])) ? Image::WIDTH : Image::HEIGHT;
+ }
+
+ if (empty($properties['height']) OR $properties['master'] === Image::WIDTH)
+ {
+ // Recalculate the height based on the width
+ $properties['height'] = round($height * $properties['width'] / $width);
+ }
+
+ if (empty($properties['width']) OR $properties['master'] === Image::HEIGHT)
+ {
+ // Recalculate the width based on the height
+ $properties['width'] = round($width * $properties['height'] / $height);
+ }
+
+ // Test if we can do a resize without resampling to speed up the final resize
+ if ($properties['width'] > $width / 2 AND $properties['height'] > $height / 2)
+ {
+ // Presize width and height
+ $pre_width = $width;
+ $pre_height = $height;
+
+ // The maximum reduction is 10% greater than the final size
+ $max_reduction_width = round($properties['width'] * 1.1);
+ $max_reduction_height = round($properties['height'] * 1.1);
+
+ // Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction
+ while ($pre_width / 2 > $max_reduction_width AND $pre_height / 2 > $max_reduction_height)
+ {
+ $pre_width /= 2;
+ $pre_height /= 2;
+ }
+
+ // Create the temporary image to copy to
+ $img = $this->imagecreatetransparent($pre_width, $pre_height);
+
+ if ($status = imagecopyresized($img, $this->tmp_image, 0, 0, 0, 0, $pre_width, $pre_height, $width, $height))
+ {
+ // Swap the new image for the old one
+ imagedestroy($this->tmp_image);
+ $this->tmp_image = $img;
+ }
+
+ // Set the width and height to the presize
+ $width = $pre_width;
+ $height = $pre_height;
+ }
+
+ // Create the temporary image to copy to
+ $img = $this->imagecreatetransparent($properties['width'], $properties['height']);
+
+ // Execute the resize
+ if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, 0, 0, $properties['width'], $properties['height'], $width, $height))
+ {
+ // Swap the new image for the old one
+ imagedestroy($this->tmp_image);
+ $this->tmp_image = $img;
+ }
+
+ return $status;
+ }
+
+ public function rotate($amount)
+ {
+ // Use current image to rotate
+ $img = $this->tmp_image;
+
+ // White, with an alpha of 0
+ $transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
+
+ // Rotate, setting the transparent color
+ $img = imagerotate($img, 360 - $amount, $transparent, -1);
+
+ // Fill the background with the transparent "color"
+ imagecolortransparent($img, $transparent);
+
+ // Merge the images
+ if ($status = imagecopymerge($this->tmp_image, $img, 0, 0, 0, 0, imagesx($this->tmp_image), imagesy($this->tmp_image), 100))
+ {
+ // Prevent the alpha from being lost
+ imagealphablending($img, TRUE);
+ imagesavealpha($img, TRUE);
+
+ // Swap the new image for the old one
+ imagedestroy($this->tmp_image);
+ $this->tmp_image = $img;
+ }
+
+ return $status;
+ }
+
+ public function sharpen($amount)
+ {
+ // Make sure that the sharpening function is available
+ if ( ! function_exists('imageconvolution'))
+ throw new Kohana_Exception('Your configured driver does not support the :method: image transformation.', array(':method:' => __FUNCTION__));
+
+ // Amount should be in the range of 18-10
+ $amount = round(abs(-18 + ($amount * 0.08)), 2);
+
+ // Gaussian blur matrix
+ $matrix = array
+ (
+ array(-1, -1, -1),
+ array(-1, $amount, -1),
+ array(-1, -1, -1),
+ );
+
+ // Perform the sharpen
+ return imageconvolution($this->tmp_image, $matrix, $amount - 8, 0);
+ }
+
+ public function composite($properties)
+ {
+ switch($properties['mime'])
+ {
+ case "image/jpeg":
+ $overlay_img = imagecreatefromjpeg($properties['overlay_file']);
+ break;
+
+ case "image/gif":
+ $overlay_img = imagecreatefromgif($properties['overlay_file']);
+ break;
+
+ case "image/png":
+ $overlay_img = imagecreatefrompng($properties['overlay_file']);
+ break;
+ }
+
+ $this->imagecopymerge_alpha($this->tmp_image, $overlay_img, $properties['x'], $properties['y'], 0, 0, imagesx($overlay_img), imagesy($overlay_img), $properties['transparency']);
+
+ imagedestroy($overlay_img);
+
+ return TRUE;
+ }
+
+ /**
+ * A replacement for php's imagecopymerge() function that supports the alpha channel
+ * See php bug #23815: http://bugs.php.net/bug.php?id=23815
+ *
+ * @param resource $dst_im Destination image link resource
+ * @param resource $src_im Source image link resource
+ * @param integer $dst_x x-coordinate of destination point
+ * @param integer $dst_y y-coordinate of destination point
+ * @param integer $src_x x-coordinate of source point
+ * @param integer $src_y y-coordinate of source point
+ * @param integer $src_w Source width
+ * @param integer $src_h Source height
+ * @param integer $pct Transparency percent (0 to 100)
+ */
+ protected function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct)
+ {
+ // Create a new blank image the site of our source image
+ $cut = imagecreatetruecolor($src_w, $src_h);
+
+ // Copy the blank image into the destination image where the source goes
+ imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h);
+
+ // Place the source image in the destination image
+ imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h);
+ imagecopymerge($dst_im, $cut, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct);
+ }
+
+ protected function properties()
+ {
+ return array(imagesx($this->tmp_image), imagesy($this->tmp_image));
+ }
+
+ /**
+ * Returns an image with a transparent background. Used for rotating to
+ * prevent unfilled backgrounds.
+ *
+ * @param integer image width
+ * @param integer image height
+ * @return resource
+ */
+ protected function imagecreatetransparent($width, $height)
+ {
+ if ($width < 1)
+ {
+ $width = 1;
+ }
+
+ if ($height < 1)
+ {
+ $height = 1;
+ }
+
+ if (self::$blank_png === NULL)
+ {
+ // Decode the blank PNG if it has not been done already
+ self::$blank_png = imagecreatefromstring(base64_decode
+ (
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29'.
+ 'mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBN'.
+ 'CgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQ'.
+ 'AANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoH'.
+ 'AgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB'.
+ '3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII='
+ ));
+
+ // Set the blank PNG width and height
+ self::$blank_png_width = imagesx(self::$blank_png);
+ self::$blank_png_height = imagesy(self::$blank_png);
+ }
+
+ $img = imagecreatetruecolor($width, $height);
+
+ // Resize the blank image
+ imagecopyresized($img, self::$blank_png, 0, 0, 0, 0, $width, $height, self::$blank_png_width, self::$blank_png_height);
+
+ // Prevent the alpha from being lost
+ imagealphablending($img, FALSE);
+ imagesavealpha($img, TRUE);
+
+ return $img;
+ }
+
+} // End Image GD Driver \ No newline at end of file
diff --git a/system/libraries/drivers/Image/GraphicsMagick.php b/system/libraries/drivers/Image/GraphicsMagick.php
new file mode 100644
index 0000000..89b40b4
--- /dev/null
+++ b/system/libraries/drivers/Image/GraphicsMagick.php
@@ -0,0 +1,225 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * GraphicsMagick Image Driver.
+ *
+ * @package Image
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Image_GraphicsMagick_Driver extends Image_Driver {
+
+ // Directory that GM is installed in
+ protected $dir = '';
+
+ // Command extension (exe for windows)
+ protected $ext = '';
+
+ // Temporary image filename
+ protected $tmp_image;
+
+ /**
+ * Attempts to detect the GraphicsMagick installation directory.
+ *
+ * @throws Kohana_Exception
+ * @param array configuration
+ * @return void
+ */
+ public function __construct($config)
+ {
+ if (empty($config['directory']))
+ {
+ // Attempt to locate GM by using "which" (only works for *nix!)
+ if ( ! is_file($path = exec('which gm')))
+ throw new Kohana_Exception('The GraphicsMagick directory specified does not contain a required program.');
+
+ $config['directory'] = dirname($path);
+ }
+
+ // Set the command extension
+ $this->ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : '';
+
+ // Check to make sure the provided path is correct
+ if ( ! is_file(realpath($config['directory']).'/gm'.$this->ext))
+ throw new Kohana_Exception('The GraphicsMagick directory specified does not contain a required program, :gm:.', array(':gm:' => 'gm'.$this->ext));
+
+
+ // Set the installation directory
+ $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
+ }
+
+ /**
+ * Creates a temporary image and executes the given actions. By creating a
+ * temporary copy of the image before manipulating it, this process is atomic.
+ */
+ public function process($image, $actions, $dir, $file, $render = FALSE, $background = NULL)
+ {
+ // Need to implement $background support
+ if ($background !== NULL)
+ throw new Kohana_Exception('The GraphicsMagick driver does not support setting a background color');
+
+ // We only need the filename
+ $image = $image['file'];
+
+ // Unique temporary filename
+ $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
+
+ // Copy the image to the temporary file
+ copy($image, $this->tmp_image);
+
+ // Quality change is done last
+ $quality = (int) arr::remove('quality', $actions);
+
+ // Use 95 for the default quality
+ empty($quality) and $quality = 95;
+
+ // All calls to these will need to be escaped, so do it now
+ $this->cmd_image = escapeshellarg($this->tmp_image);
+ $this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file);
+
+ if ($status = $this->execute($actions))
+ {
+ // Use convert to change the image into its final version. This is
+ // done to allow the file type to change correctly, and to handle
+ // the quality conversion in the most effective way possible.
+ if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
+ {
+ $this->errors[] = $error;
+ }
+ else
+ {
+ // Output the image directly to the browser
+ if ($render !== FALSE)
+ {
+ $contents = file_get_contents($this->tmp_image);
+ switch (substr($file, strrpos($file, '.') + 1))
+ {
+ case 'jpg':
+ case 'jpeg':
+ header('Content-Type: image/jpeg');
+ break;
+ case 'gif':
+ header('Content-Type: image/gif');
+ break;
+ case 'png':
+ header('Content-Type: image/png');
+ break;
+ }
+ echo $contents;
+ }
+ }
+ }
+
+ // Remove the temporary image
+ unlink($this->tmp_image);
+ $this->tmp_image = '';
+
+ return $status;
+ }
+
+ public function crop($prop)
+ {
+ // Sanitize and normalize the properties into geometry
+ $this->sanitize_geometry($prop);
+
+ // Set the IM geometry based on the properties
+ $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
+
+ if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function flip($dir)
+ {
+ // Convert the direction into a GM command
+ $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
+
+ if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function resize($prop)
+ {
+ switch ($prop['master'])
+ {
+ case Image::WIDTH: // Wx
+ $dim = escapeshellarg($prop['width'].'x');
+ break;
+ case Image::HEIGHT: // xH
+ $dim = escapeshellarg('x'.$prop['height']);
+ break;
+ case Image::AUTO: // WxH
+ $dim = escapeshellarg($prop['width'].'x'.$prop['height']);
+ break;
+ case Image::NONE: // WxH!
+ $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
+ break;
+ }
+
+ // Use "convert" to change the width and height
+ if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function rotate($amt)
+ {
+ if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function sharpen($amount)
+ {
+ // Set the sigma, radius, and amount. The amount formula allows a nice
+ // spread between 1 and 100 without pixelizing the image badly.
+ $sigma = 0.5;
+ $radius = $sigma * 2;
+ $amount = round(($amount / 80) * 3.14, 2);
+
+ // Convert the amount to an GM command
+ $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
+
+ if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function composite($properties)
+ {
+ if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' composite').' -geometry ' . escapeshellarg('+'.$properties['x'].'+'.$properties['y']).' -dissolve '.escapeshellarg($properties['transparency']).' '.escapeshellarg($properties['overlay_file']).' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ protected function properties()
+ {
+ return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
+ }
+
+} // End Image GraphicsMagick Driver \ No newline at end of file
diff --git a/system/libraries/drivers/Image/ImageMagick.php b/system/libraries/drivers/Image/ImageMagick.php
new file mode 100644
index 0000000..55c0ba2
--- /dev/null
+++ b/system/libraries/drivers/Image/ImageMagick.php
@@ -0,0 +1,233 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * ImageMagick Image Driver.
+ *
+ * $Id: ImageMagick.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Image
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Image_ImageMagick_Driver extends Image_Driver {
+
+ // Directory that IM is installed in
+ protected $dir = '';
+
+ // Command extension (exe for windows)
+ protected $ext = '';
+
+ // Temporary image filename
+ protected $tmp_image;
+
+ /**
+ * Attempts to detect the ImageMagick installation directory.
+ *
+ * @throws Kohana_Exception
+ * @param array configuration
+ * @return void
+ */
+ public function __construct($config)
+ {
+ if (empty($config['directory']))
+ {
+ // Attempt to locate IM by using "which" (only works for *nix!)
+ if ( ! is_file($path = exec('which convert')))
+ throw new Kohana_Exception('The ImageMagick directory specified does not contain a required program.');
+
+ $config['directory'] = dirname($path);
+ }
+
+ // Set the command extension
+ $this->ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : '';
+
+ // Check to make sure the provided path is correct
+ if ( ! is_file(realpath($config['directory']).'/convert'.$this->ext))
+ throw new Kohana_Exception('The ImageMagick directory specified does not contain a required program, :im:', array(':im:' => 'convert'.$this->ext));
+
+ // Set the installation directory
+ $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
+ }
+
+ /**
+ * Creates a temporary image and executes the given actions. By creating a
+ * temporary copy of the image before manipulating it, this process is atomic.
+ */
+ public function process($image, $actions, $dir, $file, $render = FALSE, $background = NULL)
+ {
+ // We only need the filename
+ $image = $image['file'];
+
+ // Unique temporary filename
+ $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
+
+ // Copy the image to the temporary file
+ copy($image, $this->tmp_image);
+
+ // Quality change is done last
+ $quality = (int) arr::remove('quality', $actions);
+
+ // Use 95 for the default quality
+ empty($quality) and $quality = 95;
+
+ if (is_string($background))
+ {
+ // Set the background color
+ $this->background = escapeshellarg($background);
+ }
+ else
+ {
+ // Use a transparent background
+ $this->background = 'transparent';
+ }
+
+ // All calls to these will need to be escaped, so do it now
+ $this->cmd_image = escapeshellarg($this->tmp_image);
+ $this->new_image = $render ? $this->cmd_image : escapeshellarg($dir.$file);
+
+ if ($status = $this->execute($actions))
+ {
+ // Use convert to change the image into its final version. This is
+ // done to allow the file type to change correctly, and to handle
+ // the quality conversion in the most effective way possible.
+ if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -background '.$this->background.' -flatten -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
+ {
+ $this->errors[] = $error;
+ }
+ else
+ {
+ // Output the image directly to the browser
+ if ($render === TRUE)
+ {
+ $contents = file_get_contents($this->tmp_image);
+ switch (substr($file, strrpos($file, '.') + 1))
+ {
+ case 'jpg':
+ case 'jpeg':
+ header('Content-Type: image/jpeg');
+ break;
+ case 'gif':
+ header('Content-Type: image/gif');
+ break;
+ case 'png':
+ header('Content-Type: image/png');
+ break;
+ }
+ echo $contents;
+ }
+ }
+ }
+
+ // Remove the temporary image
+ unlink($this->tmp_image);
+ $this->tmp_image = '';
+
+ return $status;
+ }
+
+ public function crop($prop)
+ {
+ // Sanitize and normalize the properties into geometry
+ $this->sanitize_geometry($prop);
+
+ // Set the IM geometry based on the properties
+ $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
+
+ if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -background '.$this->background.' -flatten -crop '.$geometry.'! '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function flip($dir)
+ {
+ // Convert the direction into a IM command
+ $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
+
+ if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -background '.$this->background.' -flatten '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function resize($prop)
+ {
+ switch ($prop['master'])
+ {
+ case Image::WIDTH: // Wx
+ $dim = escapeshellarg($prop['width'].'x');
+ break;
+ case Image::HEIGHT: // xH
+ $dim = escapeshellarg('x'.$prop['height']);
+ break;
+ case Image::AUTO: // WxH
+ $dim = escapeshellarg($prop['width'].'x'.$prop['height']);
+ break;
+ case Image::NONE: // WxH!
+ $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
+ break;
+ }
+
+ // Use "convert" to change the width and height
+ if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -background '.$this->background.' -flatten -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function rotate($amt)
+ {
+ if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -background '.$this->background.' -flatten -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function sharpen($amount)
+ {
+ // Set the sigma, radius, and amount. The amount formula allows a nice
+ // spread between 1 and 100 without pixelizing the image badly.
+ $sigma = 0.5;
+ $radius = $sigma * 2;
+ $amount = round(($amount / 80) * 3.14, 2);
+
+ // Convert the amount to an IM command
+ $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
+
+ if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -background '.$this->background.' -flatten -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ public function composite($properties)
+ {
+ if ($error = exec(escapeshellcmd($this->dir.'composite'.$this->ext).' -geometry ' . escapeshellarg('+'.$properties['x'].'+'.$properties['y']).' -dissolve '.escapeshellarg($properties['transparency']).' '.escapeshellarg($properties['overlay_file']).' '.$this->cmd_image.' '.$this->cmd_image))
+ {
+ $this->errors[] = $error;
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ protected function properties()
+ {
+ return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
+ }
+
+} // End Image ImageMagick Driver \ No newline at end of file
diff --git a/system/libraries/drivers/Log.php b/system/libraries/drivers/Log.php
new file mode 100644
index 0000000..cd6dba7
--- /dev/null
+++ b/system/libraries/drivers/Log.php
@@ -0,0 +1,22 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Log API driver.
+ *
+ * $Id: Log.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Kohana_Log
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+abstract class Log_Driver {
+
+ protected $config = array();
+
+ public function __construct(array $config)
+ {
+ $this->config = $config;
+ }
+
+ abstract public function save(array $messages);
+} \ No newline at end of file
diff --git a/system/libraries/drivers/Log/Database.php b/system/libraries/drivers/Log/Database.php
new file mode 100644
index 0000000..19db974
--- /dev/null
+++ b/system/libraries/drivers/Log/Database.php
@@ -0,0 +1,40 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Log API driver.
+ *
+ * $Id: Database.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Kohana_Log
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Log_Database_Driver extends Log_Driver {
+
+ public function save(array $messages)
+ {
+ $insert = db::build($this->config['group'])
+ ->insert($this->config['table'])
+ ->columns(array('date', 'level', 'message'));
+
+ $run_insert = FALSE;
+
+ foreach ($messages AS $message)
+ {
+ if ($this->config['log_levels'][$message['type']] <= $this->config['log_threshold'])
+ {
+ // Add new message to database
+ $insert->values($message);
+
+ // There is data to insert
+ $run_insert = TRUE;
+ }
+ }
+
+ // Update the database
+ if ($run_insert)
+ {
+ $insert->execute();
+ }
+ }
+} \ No newline at end of file
diff --git a/system/libraries/drivers/Log/File.php b/system/libraries/drivers/Log/File.php
new file mode 100644
index 0000000..6ad565b
--- /dev/null
+++ b/system/libraries/drivers/Log/File.php
@@ -0,0 +1,44 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Log API driver.
+ *
+ * $Id: File.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Kohana_Log
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Log_File_Driver extends Log_Driver {
+
+ public function save(array $messages)
+ {
+ // Filename of the log
+ $filename = $this->config['log_directory'].'/'.date('Y-m-d').'.log'.EXT;
+
+ if ( ! is_file($filename))
+ {
+ // Write the SYSPATH checking header
+ file_put_contents($filename,
+ '<?php defined(\'SYSPATH\') or die(\'No direct script access.\'); ?>'.PHP_EOL.PHP_EOL);
+
+ // Prevent external writes
+ chmod($filename, $this->config['posix_permissions']);
+ }
+
+ foreach ($messages AS $message)
+ {
+ if ($this->config['log_levels'][$message['type']] <= $this->config['log_threshold'])
+ {
+ // Add a new message line
+ $messages_to_write[] = date($this->config['date_format'], $message['date']).' --- '.$message['type'].': '.$message['message'];
+ }
+ }
+
+ if ( ! empty($messages_to_write))
+ {
+ // Write messages to log file
+ file_put_contents($filename, implode(PHP_EOL, $messages_to_write).PHP_EOL, FILE_APPEND);
+ }
+ }
+} \ No newline at end of file
diff --git a/system/libraries/drivers/Log/Syslog.php b/system/libraries/drivers/Log/Syslog.php
new file mode 100644
index 0000000..5da5d25
--- /dev/null
+++ b/system/libraries/drivers/Log/Syslog.php
@@ -0,0 +1,34 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Log API driver.
+ *
+ * @package Kohana_Log
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Log_Syslog_Driver extends Log_Driver {
+
+ protected $syslog_levels = array('error' => LOG_ERR,
+ 'alert' => LOG_WARNING,
+ 'info' => LOG_INFO,
+ 'debug' => LOG_DEBUG);
+
+ public function save(array $messages)
+ {
+ // Open the connection to syslog
+ openlog($this->config['ident'], LOG_CONS, LOG_USER);
+
+ do
+ {
+ // Load the next message
+ list ($date, $type, $text) = array_shift($messages);
+
+ syslog($this->syslog_levels[$type], $text);
+ }
+ while ( ! empty($messages));
+
+ // Close connection to syslog
+ closelog();
+ }
+} \ No newline at end of file
diff --git a/system/libraries/drivers/Session.php b/system/libraries/drivers/Session.php
new file mode 100644
index 0000000..e591b91
--- /dev/null
+++ b/system/libraries/drivers/Session.php
@@ -0,0 +1,70 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Session driver interface
+ *
+ * $Id: Session.php 4729 2009-12-29 20:35:19Z isaiah $
+ *
+ * @package Kohana
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+interface Session_Driver {
+
+ /**
+ * Opens a session.
+ *
+ * @param string save path
+ * @param string session name
+ * @return boolean
+ */
+ public function open($path, $name);
+
+ /**
+ * Closes a session.
+ *
+ * @return boolean
+ */
+ public function close();
+
+ /**
+ * Reads a session.
+ *
+ * @param string session id
+ * @return string
+ */
+ public function read($id);
+
+ /**
+ * Writes a session.
+ *
+ * @param string session id
+ * @param string session data
+ * @return boolean
+ */
+ public function write($id, $data);
+
+ /**
+ * Destroys a session.
+ *
+ * @param string session id
+ * @return boolean
+ */
+ public function destroy($id);
+
+ /**
+ * Regenerates the session id.
+ *
+ * @return string
+ */
+ public function regenerate();
+
+ /**
+ * Garbage collection.
+ *
+ * @param integer session expiration period
+ * @return boolean
+ */
+ public function gc($maxlifetime);
+
+} // End Session Driver Interface \ No newline at end of file
diff --git a/system/libraries/drivers/Session/Cache.php b/system/libraries/drivers/Session/Cache.php
new file mode 100644
index 0000000..459f8b0
--- /dev/null
+++ b/system/libraries/drivers/Session/Cache.php
@@ -0,0 +1,108 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Session cache driver.
+ *
+ * Cache library config goes in the session.storage config entry:
+ * $config['storage'] = array(
+ * 'driver' => 'apc',
+ * 'requests' => 10000
+ * );
+ * Lifetime does not need to be set as it is
+ * overridden by the session expiration setting.
+ *
+ * $Id: Cache.php 4729 2009-12-29 20:35:19Z isaiah $
+ *
+ * @package Kohana
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Session_Cache_Driver implements Session_Driver {
+
+ protected $cache;
+ protected $encrypt;
+
+ public function __construct()
+ {
+ // Load Encrypt library
+ if (Kohana::config('session.encryption'))
+ {
+ $this->encrypt = new Encrypt;
+ }
+
+ Kohana_Log::add('debug', 'Session Cache Driver Initialized');
+ }
+
+ public function open($path, $name)
+ {
+ $config = Kohana::config('session.storage');
+
+ if (empty($config))
+ {
+ // Load the default group
+ $config = Kohana::config('cache.default');
+ }
+ elseif (is_string($config))
+ {
+ $name = $config;
+
+ // Test the config group name
+ if (($config = Kohana::config('cache.'.$config)) === NULL)
+ throw new Kohana_Exception('The :group: group is not defined in your configuration.', array(':group:' => $name));
+ }
+
+ $config['lifetime'] = (Kohana::config('session.expiration') == 0) ? 86400 : Kohana::config('session.expiration');
+ $this->cache = new Cache($config);
+
+ return is_object($this->cache);
+ }
+
+ public function close()
+ {
+ return TRUE;
+ }
+
+ public function read($id)
+ {
+ $id = 'session_'.$id;
+ if ($data = $this->cache->get($id))
+ {
+ return Kohana::config('session.encryption') ? $this->encrypt->decode($data) : $data;
+ }
+
+ // Return value must be string, NOT a boolean
+ return '';
+ }
+
+ public function write($id, $data)
+ {
+ if ( ! Session::$should_save)
+ return TRUE;
+
+ $id = 'session_'.$id;
+ $data = Kohana::config('session.encryption') ? $this->encrypt->encode($data) : $data;
+
+ return $this->cache->set($id, $data);
+ }
+
+ public function destroy($id)
+ {
+ $id = 'session_'.$id;
+ return $this->cache->delete($id);
+ }
+
+ public function regenerate()
+ {
+ session_regenerate_id(TRUE);
+
+ // Return new session id
+ return session_id();
+ }
+
+ public function gc($maxlifetime)
+ {
+ // Just return, caches are automatically cleaned up
+ return TRUE;
+ }
+
+} // End Session Cache Driver
diff --git a/system/libraries/drivers/Session/Cookie.php b/system/libraries/drivers/Session/Cookie.php
new file mode 100644
index 0000000..88f5e21
--- /dev/null
+++ b/system/libraries/drivers/Session/Cookie.php
@@ -0,0 +1,83 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Session cookie driver.
+ *
+ * $Id: Cookie.php 4729 2009-12-29 20:35:19Z isaiah $
+ *
+ * @package Kohana
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Session_Cookie_Driver implements Session_Driver {
+
+ protected $cookie_name;
+ protected $encrypt; // Library
+
+ public function __construct()
+ {
+ $this->cookie_name = Kohana::config('session.name').'_data';
+
+ if (Kohana::config('session.encryption'))
+ {
+ $this->encrypt = Encrypt::instance();
+ }
+
+ Kohana_Log::add('debug', 'Session Cookie Driver Initialized');
+ }
+
+ public function open($path, $name)
+ {
+ return TRUE;
+ }
+
+ public function close()
+ {
+ return TRUE;
+ }
+
+ public function read($id)
+ {
+ $data = (string) cookie::get($this->cookie_name);
+
+ if ($data == '')
+ return $data;
+
+ return empty($this->encrypt) ? base64_decode($data) : $this->encrypt->decode($data);
+ }
+
+ public function write($id, $data)
+ {
+ if ( ! Session::$should_save)
+ return TRUE;
+
+ $data = empty($this->encrypt) ? base64_encode($data) : $this->encrypt->encode($data);
+
+ if (strlen($data) > 4048)
+ {
+ Kohana_Log::add('error', 'Session ('.$id.') data exceeds the 4KB limit, ignoring write.');
+ return FALSE;
+ }
+
+ return cookie::set($this->cookie_name, $data, Kohana::config('session.expiration'));
+ }
+
+ public function destroy($id)
+ {
+ return cookie::delete($this->cookie_name);
+ }
+
+ public function regenerate()
+ {
+ session_regenerate_id(TRUE);
+
+ // Return new id
+ return session_id();
+ }
+
+ public function gc($maxlifetime)
+ {
+ return TRUE;
+ }
+
+} // End Session Cookie Driver Class \ No newline at end of file
diff --git a/system/libraries/drivers/Session/Database.php b/system/libraries/drivers/Session/Database.php
new file mode 100644
index 0000000..7b372d2
--- /dev/null
+++ b/system/libraries/drivers/Session/Database.php
@@ -0,0 +1,178 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Session database driver.
+ *
+ * $Id: Database.php 4729 2009-12-29 20:35:19Z isaiah $
+ *
+ * @package Kohana
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Session_Database_Driver implements Session_Driver {
+
+ /*
+ CREATE TABLE sessions
+ (
+ session_id VARCHAR(127) NOT NULL,
+ last_activity INT(10) UNSIGNED NOT NULL,
+ data TEXT NOT NULL,
+ PRIMARY KEY (session_id)
+ );
+ */
+
+ // Database settings
+ protected $db = 'default';
+ protected $table = 'sessions';
+
+ // Encryption
+ protected $encrypt;
+
+ // Session settings
+ protected $session_id;
+ protected $written = FALSE;
+
+ public function __construct()
+ {
+ // Load configuration
+ $config = Kohana::config('session');
+
+ if ( ! empty($config['encryption']))
+ {
+ // Load encryption
+ $this->encrypt = Encrypt::instance();
+ }
+
+ if (is_array($config['storage']))
+ {
+ if ( ! empty($config['storage']['group']))
+ {
+ // Set the group name
+ $this->db = $config['storage']['group'];
+ }
+
+ if ( ! empty($config['storage']['table']))
+ {
+ // Set the table name
+ $this->table = $config['storage']['table'];
+ }
+ }
+
+ Kohana_Log::add('debug', 'Session Database Driver Initialized');
+ }
+
+ public function open($path, $name)
+ {
+ return TRUE;
+ }
+
+ public function close()
+ {
+ return TRUE;
+ }
+
+ public function read($id)
+ {
+ // Load the session
+ $query = db::select('data')
+ ->from($this->table)
+ ->where('session_id', '=', $id)
+ ->limit(1)
+ ->execute($this->db);
+
+ if ($query->count() === 0)
+ {
+ // No current session
+ $this->session_id = NULL;
+
+ return '';
+ }
+
+ // Set the current session id
+ $this->session_id = $id;
+
+ // Load the data
+ $data = $query->current()->data;
+
+ return ($this->encrypt === NULL) ? base64_decode($data) : $this->encrypt->decode($data);
+ }
+
+ public function write($id, $data)
+ {
+ if ( ! Session::$should_save)
+ return TRUE;
+
+ $data = array
+ (
+ 'session_id' => $id,
+ 'last_activity' => time(),
+ 'data' => ($this->encrypt === NULL) ? base64_encode($data) : $this->encrypt->encode($data)
+ );
+
+ if ($this->session_id === NULL)
+ {
+ // Insert a new session
+ $query = db::insert($this->table, $data)
+ ->execute($this->db);
+ }
+ elseif ($id === $this->session_id)
+ {
+ // Do not update the session_id
+ unset($data['session_id']);
+
+ // Update the existing session
+ $query = db::update($this->table)
+ ->set($data)
+ ->where('session_id', '=', $id)
+ ->execute($this->db);
+ }
+ else
+ {
+ // Update the session and id
+ $query = db::update($this->table)
+ ->set($data)
+ ->where('session_id', '=', $this->session_id)
+ ->execute($this->db);
+
+ // Set the new session id
+ $this->session_id = $id;
+ }
+
+ return (bool) $query->count();
+ }
+
+ public function destroy($id)
+ {
+ // Delete the requested session
+ db::delete($this->table)
+ ->where('session_id', '=', $id)
+ ->execute($this->db);
+
+ // Session id is no longer valid
+ $this->session_id = NULL;
+
+ return TRUE;
+ }
+
+ public function regenerate()
+ {
+ // Generate a new session id
+ session_regenerate_id();
+
+ // Return new session id
+ return session_id();
+ }
+
+ public function gc($maxlifetime)
+ {
+ // Delete all expired sessions
+ $query = db::delete($this->table)
+ ->where('last_activity', '<', time() - $maxlifetime)
+ ->execute($this->db);
+
+ Kohana_Log::add('debug', 'Session garbage collected: '.$query->count().' row(s) deleted.');
+
+ return TRUE;
+ }
+
+} // End Session Database Driver