From 9e9d77b4072de4f8c73e8bbade07a8f27734e4bd Mon Sep 17 00:00:00 2001
From: Florian Pritz '.implode(' ', ( ! is_array($message)) ? array($message) : $message).'',
+ 'heading_row_start' => '
'
+ );
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Parse Template
+ *
+ * Harvests the data within the template {pseudo-variables}
+ * used to display the calendar
+ *
+ * @access public
+ * @return void
+ */
+ function parse_template()
+ {
+ $this->temp = $this->default_template();
+
+ if ($this->template == '')
+ {
+ return;
+ }
+
+ $today = array('cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today');
+
+ foreach (array('table_open', 'table_close', 'heading_row_start', 'heading_previous_cell', 'heading_title_cell', 'heading_next_cell', 'heading_row_end', 'week_row_start', 'week_day_cell', 'week_row_end', 'cal_row_start', 'cal_cell_start', 'cal_cell_content', 'cal_cell_no_content', 'cal_cell_blank', 'cal_cell_end', 'cal_row_end', 'cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today') as $val)
+ {
+ if (preg_match("/\{".$val."\}(.*?)\{\/".$val."\}/si", $this->template, $match))
+ {
+ $this->temp[$val] = $match['1'];
+ }
+ else
+ {
+ if (in_array($val, $today, TRUE))
+ {
+ $this->temp[$val] = $this->temp[str_replace('_today', '', $val)];
+ }
+ }
+ }
+ }
+
+}
+
+// END CI_Calendar class
+
+/* End of file Calendar.php */
+/* Location: ./system/libraries/Calendar.php */
\ No newline at end of file
diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php
new file mode 100644
index 000000000..2eb8b75be
--- /dev/null
+++ b/system/libraries/Cart.php
@@ -0,0 +1,550 @@
+CI =& get_instance();
+
+ // Are any config settings being passed manually? If so, set them
+ $config = array();
+ if (count($params) > 0)
+ {
+ foreach ($params as $key => $val)
+ {
+ $config[$key] = $val;
+ }
+ }
+
+ // Load the Sessions class
+ $this->CI->load->library('session', $config);
+
+ // Grab the shopping cart array from the session table, if it exists
+ if ($this->CI->session->userdata('cart_contents') !== FALSE)
+ {
+ $this->_cart_contents = $this->CI->session->userdata('cart_contents');
+ }
+ else
+ {
+ // No cart exists so we'll set some base values
+ $this->_cart_contents['cart_total'] = 0;
+ $this->_cart_contents['total_items'] = 0;
+ }
+
+ log_message('debug', "Cart Class Initialized");
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert items into the cart and save it to the session table
+ *
+ * @access public
+ * @param array
+ * @return bool
+ */
+ function insert($items = array())
+ {
+ // Was any cart data passed? No? Bah...
+ if ( ! is_array($items) OR count($items) == 0)
+ {
+ log_message('error', 'The insert method must be passed an array containing data.');
+ return FALSE;
+ }
+
+ // You can either insert a single product using a one-dimensional array,
+ // or multiple products using a multi-dimensional one. The way we
+ // determine the array type is by looking for a required array key named "id"
+ // at the top level. If it's not found, we will assume it's a multi-dimensional array.
+
+ $save_cart = FALSE;
+ if (isset($items['id']))
+ {
+ if ($this->_insert($items) == TRUE)
+ {
+ $save_cart = TRUE;
+ }
+ }
+ else
+ {
+ foreach ($items as $val)
+ {
+ if (is_array($val) AND isset($val['id']))
+ {
+ if ($this->_insert($val) == TRUE)
+ {
+ $save_cart = TRUE;
+ }
+ }
+ }
+ }
+
+ // Save the cart data if the insert was successful
+ if ($save_cart == TRUE)
+ {
+ $this->_save_cart();
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert
+ *
+ * @access private
+ * @param array
+ * @return bool
+ */
+ function _insert($items = array())
+ {
+ // Was any cart data passed? No? Bah...
+ if ( ! is_array($items) OR count($items) == 0)
+ {
+ log_message('error', 'The insert method must be passed an array containing data.');
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ // Does the $items array contain an id, quantity, price, and name? These are required
+ if ( ! isset($items['id']) OR ! isset($items['qty']) OR ! isset($items['price']) OR ! isset($items['name']))
+ {
+ log_message('error', 'The cart array must contain a product ID, quantity, price, and name.');
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ // Prep the quantity. It can only be a number. Duh...
+ $items['qty'] = trim(preg_replace('/([^0-9])/i', '', $items['qty']));
+ // Trim any leading zeros
+ $items['qty'] = trim(preg_replace('/(^[0]+)/i', '', $items['qty']));
+
+ // If the quantity is zero or blank there's nothing for us to do
+ if ( ! is_numeric($items['qty']) OR $items['qty'] == 0)
+ {
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods
+ // Not totally sure we should impose this rule, but it seems prudent to standardize IDs.
+ // Note: These can be user-specified by setting the $this->product_id_rules variable.
+ if ( ! preg_match("/^[".$this->product_id_rules."]+$/i", $items['id']))
+ {
+ log_message('error', 'Invalid product ID. The product ID can only contain alpha-numeric characters, dashes, and underscores');
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods.
+ // Note: These can be user-specified by setting the $this->product_name_rules variable.
+ if ( ! preg_match("/^[".$this->product_name_rules."]+$/i", $items['name']))
+ {
+ log_message('error', 'An invalid name was submitted as the product name: '.$items['name'].' The name can only contain alpha-numeric characters, dashes, underscores, colons, and spaces');
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ // Prep the price. Remove anything that isn't a number or decimal point.
+ $items['price'] = trim(preg_replace('/([^0-9\.])/i', '', $items['price']));
+ // Trim any leading zeros
+ $items['price'] = trim(preg_replace('/(^[0]+)/i', '', $items['price']));
+
+ // Is the price a valid number?
+ if ( ! is_numeric($items['price']))
+ {
+ log_message('error', 'An invalid price was submitted for product ID: '.$items['id']);
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ // We now need to create a unique identifier for the item being inserted into the cart.
+ // Every time something is added to the cart it is stored in the master cart array.
+ // Each row in the cart array, however, must have a unique index that identifies not only
+ // a particular product, but makes it possible to store identical products with different options.
+ // For example, what if someone buys two identical t-shirts (same product ID), but in
+ // different sizes? The product ID (and other attributes, like the name) will be identical for
+ // both sizes because it's the same shirt. The only difference will be the size.
+ // Internally, we need to treat identical submissions, but with different options, as a unique product.
+ // Our solution is to convert the options array to a string and MD5 it along with the product ID.
+ // This becomes the unique "row ID"
+ if (isset($items['options']) AND count($items['options']) > 0)
+ {
+ $rowid = md5($items['id'].implode('', $items['options']));
+ }
+ else
+ {
+ // No options were submitted so we simply MD5 the product ID.
+ // Technically, we don't need to MD5 the ID in this case, but it makes
+ // sense to standardize the format of array indexes for both conditions
+ $rowid = md5($items['id']);
+ }
+
+ // --------------------------------------------------------------------
+
+ // Now that we have our unique "row ID", we'll add our cart items to the master array
+
+ // let's unset this first, just to make sure our index contains only the data from this submission
+ unset($this->_cart_contents[$rowid]);
+
+ // Create a new index with our new row ID
+ $this->_cart_contents[$rowid]['rowid'] = $rowid;
+
+ // And add the new items to the cart array
+ foreach ($items as $key => $val)
+ {
+ $this->_cart_contents[$rowid][$key] = $val;
+ }
+
+ // Woot!
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update the cart
+ *
+ * This function permits the quantity of a given item to be changed.
+ * Typically it is called from the "view cart" page if a user makes
+ * changes to the quantity before checkout. That array must contain the
+ * product ID and quantity for each item.
+ *
+ * @access public
+ * @param array
+ * @param string
+ * @return bool
+ */
+ function update($items = array())
+ {
+ // Was any cart data passed?
+ if ( ! is_array($items) OR count($items) == 0)
+ {
+ return FALSE;
+ }
+
+ // You can either update a single product using a one-dimensional array,
+ // or multiple products using a multi-dimensional one. The way we
+ // determine the array type is by looking for a required array key named "id".
+ // If it's not found we assume it's a multi-dimensional array
+ $save_cart = FALSE;
+ if (isset($items['rowid']) AND isset($items['qty']))
+ {
+ if ($this->_update($items) == TRUE)
+ {
+ $save_cart = TRUE;
+ }
+ }
+ else
+ {
+ foreach ($items as $val)
+ {
+ if (is_array($val) AND isset($val['rowid']) AND isset($val['qty']))
+ {
+ if ($this->_update($val) == TRUE)
+ {
+ $save_cart = TRUE;
+ }
+ }
+ }
+ }
+
+ // Save the cart data if the insert was successful
+ if ($save_cart == TRUE)
+ {
+ $this->_save_cart();
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update the cart
+ *
+ * This function permits the quantity of a given item to be changed.
+ * Typically it is called from the "view cart" page if a user makes
+ * changes to the quantity before checkout. That array must contain the
+ * product ID and quantity for each item.
+ *
+ * @access private
+ * @param array
+ * @return bool
+ */
+ function _update($items = array())
+ {
+ // Without these array indexes there is nothing we can do
+ if ( ! isset($items['qty']) OR ! isset($items['rowid']) OR ! isset($this->_cart_contents[$items['rowid']]))
+ {
+ return FALSE;
+ }
+
+ // Prep the quantity
+ $items['qty'] = preg_replace('/([^0-9])/i', '', $items['qty']);
+
+ // Is the quantity a number?
+ if ( ! is_numeric($items['qty']))
+ {
+ return FALSE;
+ }
+
+ // Is the new quantity different than what is already saved in the cart?
+ // If it's the same there's nothing to do
+ if ($this->_cart_contents[$items['rowid']]['qty'] == $items['qty'])
+ {
+ return FALSE;
+ }
+
+ // Is the quantity zero? If so we will remove the item from the cart.
+ // If the quantity is greater than zero we are updating
+ if ($items['qty'] == 0)
+ {
+ unset($this->_cart_contents[$items['rowid']]);
+ }
+ else
+ {
+ $this->_cart_contents[$items['rowid']]['qty'] = $items['qty'];
+ }
+
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Save the cart array to the session DB
+ *
+ * @access private
+ * @return bool
+ */
+ function _save_cart()
+ {
+ // Unset these so our total can be calculated correctly below
+ unset($this->_cart_contents['total_items']);
+ unset($this->_cart_contents['cart_total']);
+
+ // Lets add up the individual prices and set the cart sub-total
+ $total = 0;
+ foreach ($this->_cart_contents as $key => $val)
+ {
+ // We make sure the array contains the proper indexes
+ if ( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty']))
+ {
+ continue;
+ }
+
+ $total += ($val['price'] * $val['qty']);
+
+ // Set the subtotal
+ $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']);
+ }
+
+ // Set the cart total and total items.
+ $this->_cart_contents['total_items'] = count($this->_cart_contents);
+ $this->_cart_contents['cart_total'] = $total;
+
+ // Is our cart empty? If so we delete it from the session
+ if (count($this->_cart_contents) <= 2)
+ {
+ $this->CI->session->unset_userdata('cart_contents');
+
+ // Nothing more to do... coffee time!
+ return FALSE;
+ }
+
+ // If we made it this far it means that our cart has data.
+ // Let's pass it to the Session class so it can be stored
+ $this->CI->session->set_userdata(array('cart_contents' => $this->_cart_contents));
+
+ // Woot!
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Cart Total
+ *
+ * @access public
+ * @return integer
+ */
+ function total()
+ {
+ return $this->_cart_contents['cart_total'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Total Items
+ *
+ * Returns the total item count
+ *
+ * @access public
+ * @return integer
+ */
+ function total_items()
+ {
+ return $this->_cart_contents['total_items'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Cart Contents
+ *
+ * Returns the entire cart array
+ *
+ * @access public
+ * @return array
+ */
+ function contents()
+ {
+ $cart = $this->_cart_contents;
+
+ // Remove these so they don't create a problem when showing the cart table
+ unset($cart['total_items']);
+ unset($cart['cart_total']);
+
+ return $cart;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Has options
+ *
+ * Returns TRUE if the rowid passed to this function correlates to an item
+ * that has options associated with it.
+ *
+ * @access public
+ * @return array
+ */
+ function has_options($rowid = '')
+ {
+ if ( ! isset($this->_cart_contents[$rowid]['options']) OR count($this->_cart_contents[$rowid]['options']) === 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Product options
+ *
+ * Returns the an array of options, for a particular product row ID
+ *
+ * @access public
+ * @return array
+ */
+ function product_options($rowid = '')
+ {
+ if ( ! isset($this->_cart_contents[$rowid]['options']))
+ {
+ return array();
+ }
+
+ return $this->_cart_contents[$rowid]['options'];
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Format Number
+ *
+ * Returns the supplied number with commas and a decimal point.
+ *
+ * @access public
+ * @return integer
+ */
+ function format_number($n = '')
+ {
+ if ($n == '')
+ {
+ return '';
+ }
+
+ // Remove anything that isn't a number or decimal point.
+ $n = trim(preg_replace('/([^0-9\.])/i', '', $n));
+
+ return number_format($n, 2, '.', ',');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Destroy the cart
+ *
+ * Empties the cart and kills the session
+ *
+ * @access public
+ * @return null
+ */
+ function destroy()
+ {
+ unset($this->_cart_contents);
+
+ $this->_cart_contents['cart_total'] = 0;
+ $this->_cart_contents['total_items'] = 0;
+
+ $this->CI->session->unset_userdata('cart_contents');
+ }
+
+
+}
+// END Cart Class
+
+/* End of file Cart.php */
+/* Location: ./system/libraries/Cart.php */
\ No newline at end of file
diff --git a/system/libraries/Config.php b/system/libraries/Config.php
new file mode 100644
index 000000000..4ef278635
--- /dev/null
+++ b/system/libraries/Config.php
@@ -0,0 +1,244 @@
+config =& get_config();
+ log_message('debug', "Config Class Initialized");
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Load Config File
+ *
+ * @access public
+ * @param string the config file name
+ * @return boolean if the file was loaded correctly
+ */
+ function load($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
+ {
+ $file = ($file == '') ? 'config' : str_replace(EXT, '', $file);
+
+ if (in_array($file, $this->is_loaded, TRUE))
+ {
+ return TRUE;
+ }
+
+ if ( ! file_exists(APPPATH.'config/'.$file.EXT))
+ {
+ if ($fail_gracefully === TRUE)
+ {
+ return FALSE;
+ }
+ show_error('The configuration file '.$file.EXT.' does not exist.');
+ }
+
+ include(APPPATH.'config/'.$file.EXT);
+
+ if ( ! isset($config) OR ! is_array($config))
+ {
+ if ($fail_gracefully === TRUE)
+ {
+ return FALSE;
+ }
+ show_error('Your '.$file.EXT.' file does not appear to contain a valid configuration array.');
+ }
+
+ if ($use_sections === TRUE)
+ {
+ if (isset($this->config[$file]))
+ {
+ $this->config[$file] = array_merge($this->config[$file], $config);
+ }
+ else
+ {
+ $this->config[$file] = $config;
+ }
+ }
+ else
+ {
+ $this->config = array_merge($this->config, $config);
+ }
+
+ $this->is_loaded[] = $file;
+ unset($config);
+
+ log_message('debug', 'Config file loaded: config/'.$file.EXT);
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch a config file item
+ *
+ *
+ * @access public
+ * @param string the config item name
+ * @param string the index name
+ * @param bool
+ * @return string
+ */
+ function item($item, $index = '')
+ {
+ if ($index == '')
+ {
+ if ( ! isset($this->config[$item]))
+ {
+ return FALSE;
+ }
+
+ $pref = $this->config[$item];
+ }
+ else
+ {
+ if ( ! isset($this->config[$index]))
+ {
+ return FALSE;
+ }
+
+ if ( ! isset($this->config[$index][$item]))
+ {
+ return FALSE;
+ }
+
+ $pref = $this->config[$index][$item];
+ }
+
+ return $pref;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch a config file item - adds slash after item
+ *
+ * The second parameter allows a slash to be added to the end of
+ * the item, in the case of a path.
+ *
+ * @access public
+ * @param string the config item name
+ * @param bool
+ * @return string
+ */
+ function slash_item($item)
+ {
+ if ( ! isset($this->config[$item]))
+ {
+ return FALSE;
+ }
+
+ $pref = $this->config[$item];
+
+ if ($pref != '' && substr($pref, -1) != '/')
+ {
+ $pref .= '/';
+ }
+
+ return $pref;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Site URL
+ *
+ * @access public
+ * @param string the URI string
+ * @return string
+ */
+ function site_url($uri = '')
+ {
+ if (is_array($uri))
+ {
+ $uri = implode('/', $uri);
+ }
+
+ if ($uri == '')
+ {
+ return $this->slash_item('base_url').$this->item('index_page');
+ }
+ else
+ {
+ $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
+ return $this->slash_item('base_url').$this->slash_item('index_page').trim($uri, '/').$suffix;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * System URL
+ *
+ * @access public
+ * @return string
+ */
+ function system_url()
+ {
+ $x = explode("/", preg_replace("|/*(.+?)/*$|", "\\1", BASEPATH));
+ return $this->slash_item('base_url').end($x).'/';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set a config file item
+ *
+ * @access public
+ * @param string the config item key
+ * @param string the config item value
+ * @return void
+ */
+ function set_item($item, $value)
+ {
+ $this->config[$item] = $value;
+ }
+
+}
+
+// END CI_Config class
+
+/* End of file Config.php */
+/* Location: ./system/libraries/Config.php */
\ No newline at end of file
diff --git a/system/libraries/Controller.php b/system/libraries/Controller.php
new file mode 100644
index 000000000..5e93de3bf
--- /dev/null
+++ b/system/libraries/Controller.php
@@ -0,0 +1,127 @@
+_ci_initialize();
+ log_message('debug', "Controller Class Initialized");
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Initialize
+ *
+ * Assigns all the bases classes loaded by the front controller to
+ * variables in this class. Also calls the autoload routine.
+ *
+ * @access private
+ * @return void
+ */
+ function _ci_initialize()
+ {
+ // Assign all the class objects that were instantiated by the
+ // front controller to local class variables so that CI can be
+ // run as one big super object.
+ $classes = array(
+ 'config' => 'Config',
+ 'input' => 'Input',
+ 'benchmark' => 'Benchmark',
+ 'uri' => 'URI',
+ 'output' => 'Output',
+ 'lang' => 'Language',
+ 'router' => 'Router'
+ );
+
+ foreach ($classes as $var => $class)
+ {
+ $this->$var =& load_class($class);
+ }
+
+ // In PHP 5 the Loader class is run as a discreet
+ // class. In PHP 4 it extends the Controller
+ if (floor(phpversion()) >= 5)
+ {
+ $this->load =& load_class('Loader');
+ $this->load->_ci_autoloader();
+ }
+ else
+ {
+ $this->_ci_autoloader();
+
+ // sync up the objects since PHP4 was working from a copy
+ foreach (array_keys(get_object_vars($this)) as $attribute)
+ {
+ if (is_object($this->$attribute))
+ {
+ $this->load->$attribute =& $this->$attribute;
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Run Scaffolding
+ *
+ * @access private
+ * @return void
+ */
+ function _ci_scaffolding()
+ {
+ if ($this->_ci_scaffolding === FALSE OR $this->_ci_scaff_table === FALSE)
+ {
+ show_404('Scaffolding unavailable');
+ }
+
+ $method = ( ! in_array($this->uri->segment(3), array('add', 'insert', 'edit', 'update', 'view', 'delete', 'do_delete'), TRUE)) ? 'view' : $this->uri->segment(3);
+
+ require_once(BASEPATH.'scaffolding/Scaffolding'.EXT);
+ $scaff = new Scaffolding($this->_ci_scaff_table);
+ $scaff->$method();
+ }
+
+
+}
+// END _Controller class
+
+/* End of file Controller.php */
+/* Location: ./system/libraries/Controller.php */
\ No newline at end of file
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
new file mode 100644
index 000000000..585815934
--- /dev/null
+++ b/system/libraries/Email.php
@@ -0,0 +1,2041 @@
+ 0)
+ {
+ $this->initialize($config);
+ }
+ else
+ {
+ $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
+ $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
+ }
+
+ log_message('debug', "Email Class Initialized");
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Initialize preferences
+ *
+ * @access public
+ * @param array
+ * @return void
+ */
+ function initialize($config = array())
+ {
+ $this->clear();
+ foreach ($config as $key => $val)
+ {
+ if (isset($this->$key))
+ {
+ $method = 'set_'.$key;
+
+ if (method_exists($this, $method))
+ {
+ $this->$method($val);
+ }
+ else
+ {
+ $this->$key = $val;
+ }
+ }
+ }
+
+ $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
+ $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Initialize the Email Data
+ *
+ * @access public
+ * @return void
+ */
+ function clear($clear_attachments = FALSE)
+ {
+ $this->_subject = "";
+ $this->_body = "";
+ $this->_finalbody = "";
+ $this->_header_str = "";
+ $this->_replyto_flag = FALSE;
+ $this->_recipients = array();
+ $this->_headers = array();
+ $this->_debug_msg = array();
+
+ $this->_set_header('User-Agent', $this->useragent);
+ $this->_set_header('Date', $this->_set_date());
+
+ if ($clear_attachments !== FALSE)
+ {
+ $this->_attach_name = array();
+ $this->_attach_type = array();
+ $this->_attach_disp = array();
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set FROM
+ *
+ * @access public
+ * @param string
+ * @param string
+ * @return void
+ */
+ function from($from, $name = '')
+ {
+ if (preg_match( '/\<(.*)\>/', $from, $match))
+ {
+ $from = $match['1'];
+ }
+
+ if ($this->validate)
+ {
+ $this->validate_email($this->_str_to_array($from));
+ }
+
+ // prepare the display name
+ if ($name != '')
+ {
+ // only use Q encoding if there are characters that would require it
+ if ( ! preg_match('/[\200-\377]/', $name))
+ {
+ // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
+ $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
+ }
+ else
+ {
+ $name = $this->_prep_q_encoding($name, TRUE);
+ }
+ }
+
+ $this->_set_header('From', $name.' <'.$from.'>');
+ $this->_set_header('Return-Path', '<'.$from.'>');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Reply-to
+ *
+ * @access public
+ * @param string
+ * @param string
+ * @return void
+ */
+ function reply_to($replyto, $name = '')
+ {
+ if (preg_match( '/\<(.*)\>/', $replyto, $match))
+ {
+ $replyto = $match['1'];
+ }
+
+ if ($this->validate)
+ {
+ $this->validate_email($this->_str_to_array($replyto));
+ }
+
+ if ($name == '')
+ {
+ $name = $replyto;
+ }
+
+ if (strncmp($name, '"', 1) != 0)
+ {
+ $name = '"'.$name.'"';
+ }
+
+ $this->_set_header('Reply-To', $name.' <'.$replyto.'>');
+ $this->_replyto_flag = TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Recipients
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function to($to)
+ {
+ $to = $this->_str_to_array($to);
+ $to = $this->clean_email($to);
+
+ if ($this->validate)
+ {
+ $this->validate_email($to);
+ }
+
+ if ($this->_get_protocol() != 'mail')
+ {
+ $this->_set_header('To', implode(", ", $to));
+ }
+
+ switch ($this->_get_protocol())
+ {
+ case 'smtp' : $this->_recipients = $to;
+ break;
+ case 'sendmail' : $this->_recipients = implode(", ", $to);
+ break;
+ case 'mail' : $this->_recipients = implode(", ", $to);
+ break;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set CC
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function cc($cc)
+ {
+ $cc = $this->_str_to_array($cc);
+ $cc = $this->clean_email($cc);
+
+ if ($this->validate)
+ {
+ $this->validate_email($cc);
+ }
+
+ $this->_set_header('Cc', implode(", ", $cc));
+
+ if ($this->_get_protocol() == "smtp")
+ {
+ $this->_cc_array = $cc;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set BCC
+ *
+ * @access public
+ * @param string
+ * @param string
+ * @return void
+ */
+ function bcc($bcc, $limit = '')
+ {
+ if ($limit != '' && is_numeric($limit))
+ {
+ $this->bcc_batch_mode = TRUE;
+ $this->bcc_batch_size = $limit;
+ }
+
+ $bcc = $this->_str_to_array($bcc);
+ $bcc = $this->clean_email($bcc);
+
+ if ($this->validate)
+ {
+ $this->validate_email($bcc);
+ }
+
+ if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
+ {
+ $this->_bcc_array = $bcc;
+ }
+ else
+ {
+ $this->_set_header('Bcc', implode(", ", $bcc));
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Email Subject
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function subject($subject)
+ {
+ $subject = $this->_prep_q_encoding($subject);
+ $this->_set_header('Subject', $subject);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Body
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function message($body)
+ {
+ $this->_body = stripslashes(rtrim(str_replace("\r", "", $body)));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Assign file attachments
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function attach($filename, $disposition = 'attachment')
+ {
+ $this->_attach_name[] = $filename;
+ $this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename))));
+ $this->_attach_disp[] = $disposition; // Can also be 'inline' Not sure if it matters
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Add a Header Item
+ *
+ * @access private
+ * @param string
+ * @param string
+ * @return void
+ */
+ function _set_header($header, $value)
+ {
+ $this->_headers[$header] = $value;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Convert a String to an Array
+ *
+ * @access private
+ * @param string
+ * @return array
+ */
+ function _str_to_array($email)
+ {
+ if ( ! is_array($email))
+ {
+ if (strpos($email, ',') !== FALSE)
+ {
+ $email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY);
+ }
+ else
+ {
+ $email = trim($email);
+ settype($email, "array");
+ }
+ }
+ return $email;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Multipart Value
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function set_alt_message($str = '')
+ {
+ $this->alt_message = ($str == '') ? '' : $str;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Mailtype
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function set_mailtype($type = 'text')
+ {
+ $this->mailtype = ($type == 'html') ? 'html' : 'text';
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Wordwrap
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function set_wordwrap($wordwrap = TRUE)
+ {
+ $this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Protocol
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function set_protocol($protocol = 'mail')
+ {
+ $this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Priority
+ *
+ * @access public
+ * @param integer
+ * @return void
+ */
+ function set_priority($n = 3)
+ {
+ if ( ! is_numeric($n))
+ {
+ $this->priority = 3;
+ return;
+ }
+
+ if ($n < 1 OR $n > 5)
+ {
+ $this->priority = 3;
+ return;
+ }
+
+ $this->priority = $n;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Newline Character
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function set_newline($newline = "\n")
+ {
+ if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r")
+ {
+ $this->newline = "\n";
+ return;
+ }
+
+ $this->newline = $newline;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set CRLF
+ *
+ * @access public
+ * @param string
+ * @return void
+ */
+ function set_crlf($crlf = "\n")
+ {
+ if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r")
+ {
+ $this->crlf = "\n";
+ return;
+ }
+
+ $this->crlf = $crlf;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set Message Boundary
+ *
+ * @access private
+ * @return void
+ */
+ function _set_boundaries()
+ {
+ $this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
+ $this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get the Message ID
+ *
+ * @access private
+ * @return string
+ */
+ function _get_message_id()
+ {
+ $from = $this->_headers['Return-Path'];
+ $from = str_replace(">", "", $from);
+ $from = str_replace("<", "", $from);
+
+ return "<".uniqid('').strstr($from, '@').">";
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get Mail Protocol
+ *
+ * @access private
+ * @param bool
+ * @return string
+ */
+ function _get_protocol($return = TRUE)
+ {
+ $this->protocol = strtolower($this->protocol);
+ $this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
+
+ if ($return == TRUE)
+ {
+ return $this->protocol;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get Mail Encoding
+ *
+ * @access private
+ * @param bool
+ * @return string
+ */
+ function _get_encoding($return = TRUE)
+ {
+ $this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
+
+ foreach ($this->_base_charsets as $charset)
+ {
+ if (strncmp($charset, $this->charset, strlen($charset)) == 0)
+ {
+ $this->_encoding = '7bit';
+ }
+ }
+
+ if ($return == TRUE)
+ {
+ return $this->_encoding;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Get content type (text/html/attachment)
+ *
+ * @access private
+ * @return string
+ */
+ function _get_content_type()
+ {
+ if ($this->mailtype == 'html' && count($this->_attach_name) == 0)
+ {
+ return 'html';
+ }
+ elseif ($this->mailtype == 'html' && count($this->_attach_name) > 0)
+ {
+ return 'html-attach';
+ }
+ elseif ($this->mailtype == 'text' && count($this->_attach_name) > 0)
+ {
+ return 'plain-attach';
+ }
+ else
+ {
+ return 'plain';
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set RFC 822 Date
+ *
+ * @access private
+ * @return string
+ */
+ function _set_date()
+ {
+ $timezone = date("Z");
+ $operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';
+ $timezone = abs($timezone);
+ $timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60;
+
+ return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Mime message
+ *
+ * @access private
+ * @return string
+ */
+ function _get_mime_message()
+ {
+ return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate Email Address
+ *
+ * @access public
+ * @param string
+ * @return bool
+ */
+ function validate_email($email)
+ {
+ if ( ! is_array($email))
+ {
+ $this->_set_error_message('email_must_be_array');
+ return FALSE;
+ }
+
+ foreach ($email as $val)
+ {
+ if ( ! $this->valid_email($val))
+ {
+ $this->_set_error_message('email_invalid_address', $val);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Email Validation
+ *
+ * @access public
+ * @param string
+ * @return bool
+ */
+ function valid_email($address)
+ {
+ return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Clean Extended Email Address: Joe Smith ',
+ 'heading_previous_cell' => ' ',
+ 'week_row_start' => '<< ',
+ 'heading_title_cell' => '{heading} ',
+ 'heading_next_cell' => '>> ',
+ 'heading_row_end' => '',
+ 'week_day_cell' => ' ',
+ 'cal_row_start' => '{week_day} ',
+ 'week_row_end' => '',
+ 'cal_cell_start' => ' ',
+ 'table_close' => '',
+ 'cal_cell_start_today' => ' ',
+ 'cal_cell_content' => '{day}',
+ 'cal_cell_content_today' => '{day}',
+ 'cal_cell_no_content' => '{day}',
+ 'cal_cell_no_content_today' => '{day}',
+ 'cal_cell_blank' => ' ',
+ 'cal_cell_end' => ' ',
+ 'cal_cell_end_today' => '',
+ 'cal_row_end' => '
', $suffix = '
') + { + $this->_error_prefix = $prefix; + $this->_error_suffix = $suffix; + } + + // -------------------------------------------------------------------- + + /** + * Get Error Message + * + * Gets the error message associated with a particular field + * + * @access public + * @param string the field name + * @return void + */ + function error($field = '', $prefix = '', $suffix = '') + { + if ( ! isset($this->_field_data[$field]['error']) OR $this->_field_data[$field]['error'] == '') + { + return ''; + } + + if ($prefix == '') + { + $prefix = $this->_error_prefix; + } + + if ($suffix == '') + { + $suffix = $this->_error_suffix; + } + + return $prefix.$this->_field_data[$field]['error'].$suffix; + } + + // -------------------------------------------------------------------- + + /** + * Error String + * + * Returns the error messages as a string, wrapped in the error delimiters + * + * @access public + * @param string + * @param string + * @return str + */ + function error_string($prefix = '', $suffix = '') + { + // No errrors, validation passes! + if (count($this->_error_array) === 0) + { + return ''; + } + + if ($prefix == '') + { + $prefix = $this->_error_prefix; + } + + if ($suffix == '') + { + $suffix = $this->_error_suffix; + } + + // Generate the error string + $str = ''; + foreach ($this->_error_array as $val) + { + if ($val != '') + { + $str .= $prefix.$val.$suffix."\n"; + } + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Run the Validator + * + * This function does all the work. + * + * @access public + * @return bool + */ + function run($group = '') + { + // Do we even have any data to process? Mm? + if (count($_POST) == 0) + { + return FALSE; + } + + // Does the _field_data array containing the validation rules exist? + // If not, we look to see if they were assigned via a config file + if (count($this->_field_data) == 0) + { + // No validation rules? We're done... + if (count($this->_config_rules) == 0) + { + return FALSE; + } + + // Is there a validation rule for the particular URI being accessed? + $uri = ($group == '') ? trim($this->CI->uri->ruri_string(), '/') : $group; + + if ($uri != '' AND isset($this->_config_rules[$uri])) + { + $this->set_rules($this->_config_rules[$uri]); + } + else + { + $this->set_rules($this->_config_rules); + } + + // We're we able to set the rules correctly? + if (count($this->_field_data) == 0) + { + log_message('debug', "Unable to find validation rules"); + return FALSE; + } + } + + // Load the language file containing error messages + $this->CI->lang->load('form_validation'); + + // Cycle through the rules for each field, match the + // corresponding $_POST item and test for errors + foreach ($this->_field_data as $field => $row) + { + // Fetch the data from the corresponding $_POST array and cache it in the _field_data array. + // Depending on whether the field name is an array or a string will determine where we get it from. + + if ($row['is_array'] == TRUE) + { + $this->_field_data[$field]['postdata'] = $this->_reduce_array($_POST, $row['keys']); + } + else + { + if (isset($_POST[$field]) AND $_POST[$field] != "") + { + $this->_field_data[$field]['postdata'] = $_POST[$field]; + } + } + + $this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']); + } + + // Did we end up with any errors? + $total_errors = count($this->_error_array); + + if ($total_errors > 0) + { + $this->_safe_form_data = TRUE; + } + + // Now we need to re-set the POST data with the new, processed data + $this->_reset_post_array(); + + // No errors, validation passes! + if ($total_errors == 0) + { + return TRUE; + } + + // Validation fails + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Traverse a multidimensional $_POST array index until the data is found + * + * @access private + * @param array + * @param array + * @param integer + * @return mixed + */ + function _reduce_array($array, $keys, $i = 0) + { + if (is_array($array)) + { + if (isset($keys[$i])) + { + if (isset($array[$keys[$i]])) + { + $array = $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)); + } + else + { + return NULL; + } + } + else + { + return $array; + } + } + + return $array; + } + + // -------------------------------------------------------------------- + + /** + * Re-populate the _POST array with our finalized and processed data + * + * @access private + * @return null + */ + function _reset_post_array() + { + foreach ($this->_field_data as $field => $row) + { + if ( ! is_null($row['postdata'])) + { + if ($row['is_array'] == FALSE) + { + if (isset($_POST[$row['field']])) + { + $_POST[$row['field']] = $this->prep_for_form($row['postdata']); + } + } + else + { + // start with a reference + $post_ref =& $_POST; + + // before we assign values, make a reference to the right POST key + if (count($row['keys']) == 1) + { + $post_ref =& $post_ref[current($row['keys'])]; + } + else + { + foreach ($row['keys'] as $val) + { + $post_ref =& $post_ref[$val]; + } + } + + if (is_array($row['postdata'])) + { + $array = array(); + foreach ($row['postdata'] as $k => $v) + { + $array[$k] = $this->prep_for_form($v); + } + + $post_ref = $array; + } + else + { + $post_ref = $this->prep_for_form($row['postdata']); + } + } + } + } + } + + // -------------------------------------------------------------------- + + /** + * Executes the Validation routines + * + * @access private + * @param array + * @param array + * @param mixed + * @param integer + * @return mixed + */ + function _execute($row, $rules, $postdata = NULL, $cycles = 0) + { + // If the $_POST data is an array we will run a recursive call + if (is_array($postdata)) + { + foreach ($postdata as $key => $val) + { + $this->_execute($row, $rules, $val, $cycles); + $cycles++; + } + + return; + } + + // -------------------------------------------------------------------- + + // If the field is blank, but NOT required, no further tests are necessary + $callback = FALSE; + if ( ! in_array('required', $rules) AND is_null($postdata)) + { + // Before we bail out, does the rule contain a callback? + if (preg_match("/(callback_\w+)/", implode(' ', $rules), $match)) + { + $callback = TRUE; + $rules = (array('1' => $match[1])); + } + else + { + return; + } + } + + // -------------------------------------------------------------------- + + // Isset Test. Typically this rule will only apply to checkboxes. + if (is_null($postdata) AND $callback == FALSE) + { + if (in_array('isset', $rules, TRUE) OR in_array('required', $rules)) + { + // Set the message type + $type = (in_array('required', $rules)) ? 'required' : 'isset'; + + if ( ! isset($this->_error_messages[$type])) + { + if (FALSE === ($line = $this->CI->lang->line($type))) + { + $line = 'The field was not set'; + } + } + else + { + $line = $this->_error_messages[$type]; + } + + // Build the error message + $message = sprintf($line, $this->_translate_fieldname($row['label'])); + + // Save the error message + $this->_field_data[$row['field']]['error'] = $message; + + if ( ! isset($this->_error_array[$row['field']])) + { + $this->_error_array[$row['field']] = $message; + } + } + + return; + } + + // -------------------------------------------------------------------- + + // Cycle through each rule and run it + foreach ($rules As $rule) + { + $_in_array = FALSE; + + // We set the $postdata variable with the current data in our master array so that + // each cycle of the loop is dealing with the processed data from the last cycle + if ($row['is_array'] == TRUE AND is_array($this->_field_data[$row['field']]['postdata'])) + { + // We shouldn't need this safety, but just in case there isn't an array index + // associated with this cycle we'll bail out + if ( ! isset($this->_field_data[$row['field']]['postdata'][$cycles])) + { + continue; + } + + $postdata = $this->_field_data[$row['field']]['postdata'][$cycles]; + $_in_array = TRUE; + } + else + { + $postdata = $this->_field_data[$row['field']]['postdata']; + } + + // -------------------------------------------------------------------- + + // Is the rule a callback? + $callback = FALSE; + if (substr($rule, 0, 9) == 'callback_') + { + $rule = substr($rule, 9); + $callback = TRUE; + } + + // Strip the parameter (if exists) from the rule + // Rules can contain a parameter: max_length[5] + $param = FALSE; + if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match)) + { + $rule = $match[1]; + $param = $match[2]; + } + + // Call the function that corresponds to the rule + if ($callback === TRUE) + { + if ( ! method_exists($this->CI, $rule)) + { + continue; + } + + // Run the function and grab the result + $result = $this->CI->$rule($postdata, $param); + + // Re-assign the result to the master data array + if ($_in_array == TRUE) + { + $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + } + else + { + $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; + } + + // If the field isn't required and we just processed a callback we'll move on... + if ( ! in_array('required', $rules, TRUE) AND $result !== FALSE) + { + continue; + } + } + else + { + if ( ! method_exists($this, $rule)) + { + // If our own wrapper function doesn't exist we see if a native PHP function does. + // Users can use any native PHP function call that has one param. + if (function_exists($rule)) + { + $result = $rule($postdata); + + if ($_in_array == TRUE) + { + $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + } + else + { + $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; + } + } + + continue; + } + + $result = $this->$rule($postdata, $param); + + if ($_in_array == TRUE) + { + $this->_field_data[$row['field']]['postdata'][$cycles] = (is_bool($result)) ? $postdata : $result; + } + else + { + $this->_field_data[$row['field']]['postdata'] = (is_bool($result)) ? $postdata : $result; + } + } + + // Did the rule test negatively? If so, grab the error. + if ($result === FALSE) + { + if ( ! isset($this->_error_messages[$rule])) + { + if (FALSE === ($line = $this->CI->lang->line($rule))) + { + $line = 'Unable to access an error message corresponding to your field name.'; + } + } + else + { + $line = $this->_error_messages[$rule]; + } + + // Is the parameter we are inserting into the error message the name + // of another field? If so we need to grab its "field label" + if (isset($this->_field_data[$param]) AND isset($this->_field_data[$param]['label'])) + { + $param = $this->_field_data[$param]['label']; + } + + // Build the error message + $message = sprintf($line, $this->_translate_fieldname($row['label']), $param); + + // Save the error message + $this->_field_data[$row['field']]['error'] = $message; + + if ( ! isset($this->_error_array[$row['field']])) + { + $this->_error_array[$row['field']] = $message; + } + + return; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Translate a field name + * + * @access private + * @param string the field name + * @return string + */ + function _translate_fieldname($fieldname) + { + // Do we need to translate the field name? + // We look for the prefix lang: to determine this + if (substr($fieldname, 0, 5) == 'lang:') + { + // Grab the variable + $line = substr($fieldname, 5); + + // Were we able to translate the field name? If not we use $line + if (FALSE === ($fieldname = $this->CI->lang->line($line))) + { + return $line; + } + } + + return $fieldname; + } + + // -------------------------------------------------------------------- + + /** + * Get the value from a form + * + * Permits you to repopulate a form field with the value it was submitted + * with, or, if that value doesn't exist, with the default + * + * @access public + * @param string the field name + * @param string + * @return void + */ + function set_value($field = '', $default = '') + { + if ( ! isset($this->_field_data[$field])) + { + return $default; + } + + return $this->_field_data[$field]['postdata']; + } + + // -------------------------------------------------------------------- + + /** + * Set Select + * + * Enables pull-down lists to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_select($field = '', $value = '', $default = FALSE) + { + if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + { + if ($default === TRUE AND count($this->_field_data) === 0) + { + return ' selected="selected"'; + } + return ''; + } + + $field = $this->_field_data[$field]['postdata']; + + if (is_array($field)) + { + if ( ! in_array($value, $field)) + { + return ''; + } + } + else + { + if (($field == '' OR $value == '') OR ($field != $value)) + { + return ''; + } + } + + return ' selected="selected"'; + } + + // -------------------------------------------------------------------- + + /** + * Set Radio + * + * Enables radio buttons to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_radio($field = '', $value = '', $default = FALSE) + { + if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + { + if ($default === TRUE AND count($this->_field_data) === 0) + { + return ' checked="checked"'; + } + return ''; + } + + $field = $this->_field_data[$field]['postdata']; + + if (is_array($field)) + { + if ( ! in_array($value, $field)) + { + return ''; + } + } + else + { + if (($field == '' OR $value == '') OR ($field != $value)) + { + return ''; + } + } + + return ' checked="checked"'; + } + + // -------------------------------------------------------------------- + + /** + * Set Checkbox + * + * Enables checkboxes to be set to the value the user + * selected in the event of an error + * + * @access public + * @param string + * @param string + * @return string + */ + function set_checkbox($field = '', $value = '', $default = FALSE) + { + if ( ! isset($this->_field_data[$field]) OR ! isset($this->_field_data[$field]['postdata'])) + { + if ($default === TRUE AND count($this->_field_data) === 0) + { + return ' checked="checked"'; + } + return ''; + } + + $field = $this->_field_data[$field]['postdata']; + + if (is_array($field)) + { + if ( ! in_array($value, $field)) + { + return ''; + } + } + else + { + if (($field == '' OR $value == '') OR ($field != $value)) + { + return ''; + } + } + + return ' checked="checked"'; + } + + // -------------------------------------------------------------------- + + /** + * Required + * + * @access public + * @param string + * @return bool + */ + function required($str) + { + if ( ! is_array($str)) + { + return (trim($str) == '') ? FALSE : TRUE; + } + else + { + return ( ! empty($str)); + } + } + + // -------------------------------------------------------------------- + + /** + * Match one field to another + * + * @access public + * @param string + * @param field + * @return bool + */ + function matches($str, $field) + { + if ( ! isset($_POST[$field])) + { + return FALSE; + } + + $field = $_POST[$field]; + + return ($str !== $field) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Minimum Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function min_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) < $val) ? FALSE : TRUE; + } + + return (strlen($str) < $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Max Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function max_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) > $val) ? FALSE : TRUE; + } + + return (strlen($str) > $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Exact Length + * + * @access public + * @param string + * @param value + * @return bool + */ + function exact_length($str, $val) + { + if (preg_match("/[^0-9]/", $val)) + { + return FALSE; + } + + if (function_exists('mb_strlen')) + { + return (mb_strlen($str) != $val) ? FALSE : TRUE; + } + + return (strlen($str) != $val) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Email + * + * @access public + * @param string + * @return bool + */ + function valid_email($str) + { + return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Emails + * + * @access public + * @param string + * @return bool + */ + function valid_emails($str) + { + if (strpos($str, ',') === FALSE) + { + return $this->valid_email(trim($str)); + } + + foreach(explode(',', $str) as $email) + { + if (trim($email) != '' && $this->valid_email(trim($email)) === FALSE) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Validate IP Address + * + * @access public + * @param string + * @return string + */ + function valid_ip($ip) + { + return $this->CI->input->valid_ip($ip); + } + + // -------------------------------------------------------------------- + + /** + * Alpha + * + * @access public + * @param string + * @return bool + */ + function alpha($str) + { + return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Alpha-numeric + * + * @access public + * @param string + * @return bool + */ + function alpha_numeric($str) + { + return ( ! preg_match("/^([a-z0-9])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Alpha-numeric with underscores and dashes + * + * @access public + * @param string + * @return bool + */ + function alpha_dash($str) + { + return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Numeric + * + * @access public + * @param string + * @return bool + */ + function numeric($str) + { + return (bool)preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str); + + } + + // -------------------------------------------------------------------- + + /** + * Is Numeric + * + * @access public + * @param string + * @return bool + */ + function is_numeric($str) + { + return ( ! is_numeric($str)) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Integer + * + * @access public + * @param string + * @return bool + */ + function integer($str) + { + return (bool)preg_match( '/^[\-+]?[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Is a Natural number (0,1,2,3, etc.) + * + * @access public + * @param string + * @return bool + */ + function is_natural($str) + { + return (bool)preg_match( '/^[0-9]+$/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Is a Natural number, but not a zero (1,2,3, etc.) + * + * @access public + * @param string + * @return bool + */ + function is_natural_no_zero($str) + { + if ( ! preg_match( '/^[0-9]+$/', $str)) + { + return FALSE; + } + + if ($str == 0) + { + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Valid Base64 + * + * Tests a string for characters outside of the Base64 alphabet + * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 + * + * @access public + * @param string + * @return bool + */ + function valid_base64($str) + { + return (bool) ! preg_match('/[^a-zA-Z0-9\/\+=]/', $str); + } + + // -------------------------------------------------------------------- + + /** + * Prep data for form + * + * This function allows HTML to be safely shown in a form. + * Special characters are converted. + * + * @access public + * @param string + * @return string + */ + function prep_for_form($data = '') + { + if (is_array($data)) + { + foreach ($data as $key => $val) + { + $data[$key] = $this->prep_for_form($val); + } + + return $data; + } + + if ($this->_safe_form_data == FALSE OR $data === '') + { + return $data; + } + + return str_replace(array("'", '"', '<', '>'), array("'", """, '<', '>'), stripslashes($data)); + } + + // -------------------------------------------------------------------- + + /** + * Prep URL + * + * @access public + * @param string + * @return string + */ + function prep_url($str = '') + { + if ($str == 'http://' OR $str == '') + { + return ''; + } + + if (substr($str, 0, 7) != 'http://' && substr($str, 0, 8) != 'https://') + { + $str = 'http://'.$str; + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Strip Image Tags + * + * @access public + * @param string + * @return string + */ + function strip_image_tags($str) + { + return $this->CI->input->strip_image_tags($str); + } + + // -------------------------------------------------------------------- + + /** + * XSS Clean + * + * @access public + * @param string + * @return string + */ + function xss_clean($str) + { + return $this->CI->input->xss_clean($str); + } + + // -------------------------------------------------------------------- + + /** + * Convert PHP tags to entities + * + * @access public + * @param string + * @return string + */ + function encode_php_tags($str) + { + return str_replace(array(''), array('<?php', '<?PHP', '<?', '?>'), $str); + } + +} +// END Form Validation Class + +/* End of file Form_validation.php */ +/* Location: ./system/libraries/Form_validation.php */ \ No newline at end of file diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php new file mode 100644 index 000000000..6fb170496 --- /dev/null +++ b/system/libraries/Ftp.php @@ -0,0 +1,618 @@ + 0) + { + $this->initialize($config); + } + + log_message('debug', "FTP Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize preferences + * + * @access public + * @param array + * @return void + */ + function initialize($config = array()) + { + foreach ($config as $key => $val) + { + if (isset($this->$key)) + { + $this->$key = $val; + } + } + + // Prep the hostname + $this->hostname = preg_replace('|.+?://|', '', $this->hostname); + } + + // -------------------------------------------------------------------- + + /** + * FTP Connect + * + * @access public + * @param array the connection values + * @return bool + */ + function connect($config = array()) + { + if (count($config) > 0) + { + $this->initialize($config); + } + + if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port))) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_connect'); + } + return FALSE; + } + + if ( ! $this->_login()) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_login'); + } + return FALSE; + } + + // Set passive mode if needed + if ($this->passive == TRUE) + { + ftp_pasv($this->conn_id, TRUE); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * FTP Login + * + * @access private + * @return bool + */ + function _login() + { + return @ftp_login($this->conn_id, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Validates the connection ID + * + * @access private + * @return bool + */ + function _is_conn() + { + if ( ! is_resource($this->conn_id)) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_no_connection'); + } + return FALSE; + } + return TRUE; + } + + // -------------------------------------------------------------------- + + + /** + * Change directory + * + * The second parameter lets us momentarily turn off debugging so that + * this function can be used to test for the existence of a folder + * without throwing an error. There's no FTP equivalent to is_dir() + * so we do it by trying to change to a particular directory. + * Internally, this parameter is only used by the "mirror" function below. + * + * @access public + * @param string + * @param bool + * @return bool + */ + function changedir($path = '', $supress_debug = FALSE) + { + if ($path == '' OR ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_chdir($this->conn_id, $path); + + if ($result === FALSE) + { + if ($this->debug == TRUE AND $supress_debug == FALSE) + { + $this->_error('ftp_unable_to_changedir'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Create a directory + * + * @access public + * @param string + * @return bool + */ + function mkdir($path = '', $permissions = NULL) + { + if ($path == '' OR ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_mkdir($this->conn_id, $path); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_makdir'); + } + return FALSE; + } + + // Set file permissions if needed + if ( ! is_null($permissions)) + { + $this->chmod($path, (int)$permissions); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Upload a file to the server + * + * @access public + * @param string + * @param string + * @param string + * @return bool + */ + function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + if ( ! file_exists($locpath)) + { + $this->_error('ftp_no_source_file'); + return FALSE; + } + + // Set the mode if not specified + if ($mode == 'auto') + { + // Get the file extension so we can set the upload type + $ext = $this->_getext($locpath); + $mode = $this->_settype($ext); + } + + $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY; + + $result = @ftp_put($this->conn_id, $rempath, $locpath, $mode); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_upload'); + } + return FALSE; + } + + // Set file permissions if needed + if ( ! is_null($permissions)) + { + $this->chmod($rempath, (int)$permissions); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rename (or move) a file + * + * @access public + * @param string + * @param string + * @param bool + * @return bool + */ + function rename($old_file, $new_file, $move = FALSE) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_rename($this->conn_id, $old_file, $new_file); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $msg = ($move == FALSE) ? 'ftp_unable_to_rename' : 'ftp_unable_to_move'; + + $this->_error($msg); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Move a file + * + * @access public + * @param string + * @param string + * @return bool + */ + function move($old_file, $new_file) + { + return $this->rename($old_file, $new_file, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Rename (or move) a file + * + * @access public + * @param string + * @return bool + */ + function delete_file($filepath) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + $result = @ftp_delete($this->conn_id, $filepath); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_delete'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Delete a folder and recursively delete everything (including sub-folders) + * containted within it. + * + * @access public + * @param string + * @return bool + */ + function delete_dir($filepath) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + // Add a trailing slash to the file path if needed + $filepath = preg_replace("/(.+?)\/*$/", "\\1/", $filepath); + + $list = $this->list_files($filepath); + + if ($list !== FALSE AND count($list) > 0) + { + foreach ($list as $item) + { + // If we can't delete the item it's probaly a folder so + // we'll recursively call delete_dir() + if ( ! @ftp_delete($this->conn_id, $item)) + { + $this->delete_dir($item); + } + } + } + + $result = @ftp_rmdir($this->conn_id, $filepath); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_delete'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set file permissions + * + * @access public + * @param string the file path + * @param string the permissions + * @return bool + */ + function chmod($path, $perm) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + // Permissions can only be set when running PHP 5 + if ( ! function_exists('ftp_chmod')) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_chmod'); + } + return FALSE; + } + + $result = @ftp_chmod($this->conn_id, $perm, $path); + + if ($result === FALSE) + { + if ($this->debug == TRUE) + { + $this->_error('ftp_unable_to_chmod'); + } + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * FTP List files in the specified directory + * + * @access public + * @return array + */ + function list_files($path = '.') + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + return ftp_nlist($this->conn_id, $path); + } + + // ------------------------------------------------------------------------ + + /** + * Read a directory and recreate it remotely + * + * This function recursively reads a folder and everything it contains (including + * sub-folders) and creates a mirror via FTP based on it. Whatever the directory structure + * of the original file path will be recreated on the server. + * + * @access public + * @param string path to source with trailing slash + * @param string path to destination - include the base folder with trailing slash + * @return bool + */ + function mirror($locpath, $rempath) + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + // Open the local file path + if ($fp = @opendir($locpath)) + { + // Attempt to open the remote file path. + if ( ! $this->changedir($rempath, TRUE)) + { + // If it doesn't exist we'll attempt to create the direcotory + if ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath)) + { + return FALSE; + } + } + + // Recursively read the local directory + while (FALSE !== ($file = readdir($fp))) + { + if (@is_dir($locpath.$file) && substr($file, 0, 1) != '.') + { + $this->mirror($locpath.$file."/", $rempath.$file."/"); + } + elseif (substr($file, 0, 1) != ".") + { + // Get the file extension so we can se the upload type + $ext = $this->_getext($file); + $mode = $this->_settype($ext); + + $this->upload($locpath.$file, $rempath.$file, $mode); + } + } + return TRUE; + } + + return FALSE; + } + + + // -------------------------------------------------------------------- + + /** + * Extract the file extension + * + * @access private + * @param string + * @return string + */ + function _getext($filename) + { + if (FALSE === strpos($filename, '.')) + { + return 'txt'; + } + + $x = explode('.', $filename); + return end($x); + } + + + // -------------------------------------------------------------------- + + /** + * Set the upload type + * + * @access private + * @param string + * @return string + */ + function _settype($ext) + { + $text_types = array( + 'txt', + 'text', + 'php', + 'phps', + 'php4', + 'js', + 'css', + 'htm', + 'html', + 'phtml', + 'shtml', + 'log', + 'xml' + ); + + + return (in_array($ext, $text_types)) ? 'ascii' : 'binary'; + } + + // ------------------------------------------------------------------------ + + /** + * Close the connection + * + * @access public + * @param string path to source + * @param string path to destination + * @return bool + */ + function close() + { + if ( ! $this->_is_conn()) + { + return FALSE; + } + + @ftp_close($this->conn_id); + } + + // ------------------------------------------------------------------------ + + /** + * Display error message + * + * @access private + * @param string + * @return bool + */ + function _error($line) + { + $CI =& get_instance(); + $CI->lang->load('ftp'); + show_error($CI->lang->line($line)); + } + + +} +// END FTP Class + +/* End of file Ftp.php */ +/* Location: ./system/libraries/Ftp.php */ \ No newline at end of file diff --git a/system/libraries/Hooks.php b/system/libraries/Hooks.php new file mode 100644 index 000000000..0b5d46809 --- /dev/null +++ b/system/libraries/Hooks.php @@ -0,0 +1,226 @@ +_initialize(); + log_message('debug', "Hooks Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize the Hooks Preferences + * + * @access private + * @return void + */ + function _initialize() + { + $CFG =& load_class('Config'); + + // If hooks are not enabled in the config file + // there is nothing else to do + + if ($CFG->item('enable_hooks') == FALSE) + { + return; + } + + // Grab the "hooks" definition file. + // If there are no hooks, we're done. + + @include(APPPATH.'config/hooks'.EXT); + + if ( ! isset($hook) OR ! is_array($hook)) + { + return; + } + + $this->hooks =& $hook; + $this->enabled = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Call Hook + * + * Calls a particular hook + * + * @access private + * @param string the hook name + * @return mixed + */ + function _call_hook($which = '') + { + if ( ! $this->enabled OR ! isset($this->hooks[$which])) + { + return FALSE; + } + + if (isset($this->hooks[$which][0]) AND is_array($this->hooks[$which][0])) + { + foreach ($this->hooks[$which] as $val) + { + $this->_run_hook($val); + } + } + else + { + $this->_run_hook($this->hooks[$which]); + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Run Hook + * + * Runs a particular hook + * + * @access private + * @param array the hook details + * @return bool + */ + function _run_hook($data) + { + if ( ! is_array($data)) + { + return FALSE; + } + + // ----------------------------------- + // Safety - Prevents run-away loops + // ----------------------------------- + + // If the script being called happens to have the same + // hook call within it a loop can happen + + if ($this->in_progress == TRUE) + { + return; + } + + // ----------------------------------- + // Set file path + // ----------------------------------- + + if ( ! isset($data['filepath']) OR ! isset($data['filename'])) + { + return FALSE; + } + + $filepath = APPPATH.$data['filepath'].'/'.$data['filename']; + + if ( ! file_exists($filepath)) + { + return FALSE; + } + + // ----------------------------------- + // Set class/function name + // ----------------------------------- + + $class = FALSE; + $function = FALSE; + $params = ''; + + if (isset($data['class']) AND $data['class'] != '') + { + $class = $data['class']; + } + + if (isset($data['function'])) + { + $function = $data['function']; + } + + if (isset($data['params'])) + { + $params = $data['params']; + } + + if ($class === FALSE AND $function === FALSE) + { + return FALSE; + } + + // ----------------------------------- + // Set the in_progress flag + // ----------------------------------- + + $this->in_progress = TRUE; + + // ----------------------------------- + // Call the requested class and/or function + // ----------------------------------- + + if ($class !== FALSE) + { + if ( ! class_exists($class)) + { + require($filepath); + } + + $HOOK = new $class; + $HOOK->$function($params); + } + else + { + if ( ! function_exists($function)) + { + require($filepath); + } + + $function($params); + } + + $this->in_progress = FALSE; + return TRUE; + } + +} + +// END CI_Hooks class + +/* End of file Hooks.php */ +/* Location: ./system/libraries/Hooks.php */ \ No newline at end of file diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php new file mode 100644 index 000000000..169c6e489 --- /dev/null +++ b/system/libraries/Image_lib.php @@ -0,0 +1,1544 @@ + 0) + { + $this->initialize($props); + } + + log_message('debug', "Image Lib Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Initialize image properties + * + * Resets values in case this class is used in a loop + * + * @access public + * @return void + */ + function clear() + { + $props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity'); + + foreach ($props as $val) + { + $this->$val = ''; + } + + // special consideration for master_dim + $this->master_dim = 'auto'; + } + + // -------------------------------------------------------------------- + + /** + * initialize image preferences + * + * @access public + * @param array + * @return bool + */ + function initialize($props = array()) + { + /* + * Convert array elements into class variables + */ + if (count($props) > 0) + { + foreach ($props as $key => $val) + { + $this->$key = $val; + } + } + + /* + * Is there a source image? + * + * If not, there's no reason to continue + * + */ + if ($this->source_image == '') + { + $this->set_error('imglib_source_image_required'); + return FALSE; + } + + /* + * Is getimagesize() Available? + * + * We use it to determine the image properties (width/height). + * Note: We need to figure out how to determine image + * properties using ImageMagick and NetPBM + * + */ + if ( ! function_exists('getimagesize')) + { + $this->set_error('imglib_gd_required_for_props'); + return FALSE; + } + + $this->image_library = strtolower($this->image_library); + + /* + * Set the full server path + * + * The source image may or may not contain a path. + * Either way, we'll try use realpath to generate the + * full server path in order to more reliably read it. + * + */ + if (function_exists('realpath') AND @realpath($this->source_image) !== FALSE) + { + $full_source_path = str_replace("\\", "/", realpath($this->source_image)); + } + else + { + $full_source_path = $this->source_image; + } + + $x = explode('/', $full_source_path); + $this->source_image = end($x); + $this->source_folder = str_replace($this->source_image, '', $full_source_path); + + // Set the Image Properties + if ( ! $this->get_image_properties($this->source_folder.$this->source_image)) + { + return FALSE; + } + + /* + * Assign the "new" image name/path + * + * If the user has set a "new_image" name it means + * we are making a copy of the source image. If not + * it means we are altering the original. We'll + * set the destination filename and path accordingly. + * + */ + if ($this->new_image == '') + { + $this->dest_image = $this->source_image; + $this->dest_folder = $this->source_folder; + } + else + { + if (strpos($this->new_image, '/') === FALSE) + { + $this->dest_folder = $this->source_folder; + $this->dest_image = $this->new_image; + } + else + { + if (function_exists('realpath') AND @realpath($this->new_image) !== FALSE) + { + $full_dest_path = str_replace("\\", "/", realpath($this->new_image)); + } + else + { + $full_dest_path = $this->new_image; + } + + // Is there a file name? + if ( ! preg_match("#\.(jpg|jpeg|gif|png)$#i", $full_dest_path)) + { + $this->dest_folder = $full_dest_path.'/'; + $this->dest_image = $this->source_image; + } + else + { + $x = explode('/', $full_dest_path); + $this->dest_image = end($x); + $this->dest_folder = str_replace($this->dest_image, '', $full_dest_path); + } + } + } + + /* + * Compile the finalized filenames/paths + * + * We'll create two master strings containing the + * full server path to the source image and the + * full server path to the destination image. + * We'll also split the destination image name + * so we can insert the thumbnail marker if needed. + * + */ + if ($this->create_thumb === FALSE OR $this->thumb_marker == '') + { + $this->thumb_marker = ''; + } + + $xp = $this->explode_name($this->dest_image); + + $filename = $xp['name']; + $file_ext = $xp['ext']; + + $this->full_src_path = $this->source_folder.$this->source_image; + $this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext; + + /* + * Should we maintain image proportions? + * + * When creating thumbs or copies, the target width/height + * might not be in correct proportion with the source + * image's width/height. We'll recalculate it here. + * + */ + if ($this->maintain_ratio === TRUE && ($this->width != '' AND $this->height != '')) + { + $this->image_reproportion(); + } + + /* + * Was a width and height specified? + * + * If the destination width/height was + * not submitted we will use the values + * from the actual file + * + */ + if ($this->width == '') + $this->width = $this->orig_width; + + if ($this->height == '') + $this->height = $this->orig_height; + + // Set the quality + $this->quality = trim(str_replace("%", "", $this->quality)); + + if ($this->quality == '' OR $this->quality == 0 OR ! is_numeric($this->quality)) + $this->quality = 90; + + // Set the x/y coordinates + $this->x_axis = ($this->x_axis == '' OR ! is_numeric($this->x_axis)) ? 0 : $this->x_axis; + $this->y_axis = ($this->y_axis == '' OR ! is_numeric($this->y_axis)) ? 0 : $this->y_axis; + + // Watermark-related Stuff... + if ($this->wm_font_color != '') + { + if (strlen($this->wm_font_color) == 6) + { + $this->wm_font_color = '#'.$this->wm_font_color; + } + } + + if ($this->wm_shadow_color != '') + { + if (strlen($this->wm_shadow_color) == 6) + { + $this->wm_shadow_color = '#'.$this->wm_shadow_color; + } + } + + if ($this->wm_overlay_path != '') + { + $this->wm_overlay_path = str_replace("\\", "/", realpath($this->wm_overlay_path)); + } + + if ($this->wm_shadow_color != '') + { + $this->wm_use_drop_shadow = TRUE; + } + + if ($this->wm_font_path != '') + { + $this->wm_use_truetype = TRUE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Resize + * + * This is a wrapper function that chooses the proper + * resize function based on the protocol specified + * + * @access public + * @return bool + */ + function resize() + { + $protocol = 'image_process_'.$this->image_library; + + if (preg_match('/gd2$/i', $protocol)) + { + $protocol = 'image_process_gd'; + } + + return $this->$protocol('resize'); + } + + // -------------------------------------------------------------------- + + /** + * Image Crop + * + * This is a wrapper function that chooses the proper + * cropping function based on the protocol specified + * + * @access public + * @return bool + */ + function crop() + { + $protocol = 'image_process_'.$this->image_library; + + if (preg_match('/gd2$/i', $protocol)) + { + $protocol = 'image_process_gd'; + } + + return $this->$protocol('crop'); + } + + // -------------------------------------------------------------------- + + /** + * Image Rotate + * + * This is a wrapper function that chooses the proper + * rotation function based on the protocol specified + * + * @access public + * @return bool + */ + function rotate() + { + // Allowed rotation values + $degs = array(90, 180, 270, 'vrt', 'hor'); + + if ($this->rotation_angle == '' OR ! in_array($this->rotation_angle, $degs)) + { + $this->set_error('imglib_rotation_angle_required'); + return FALSE; + } + + // Reassign the width and height + if ($this->rotation_angle == 90 OR $this->rotation_angle == 270) + { + $this->width = $this->orig_height; + $this->height = $this->orig_width; + } + else + { + $this->width = $this->orig_width; + $this->height = $this->orig_height; + } + + + // Choose resizing function + if ($this->image_library == 'imagemagick' OR $this->image_library == 'netpbm') + { + $protocol = 'image_process_'.$this->image_library; + + return $this->$protocol('rotate'); + } + + if ($this->rotation_angle == 'hor' OR $this->rotation_angle == 'vrt') + { + return $this->image_mirror_gd(); + } + else + { + return $this->image_rotate_gd(); + } + } + + // -------------------------------------------------------------------- + + /** + * Image Process Using GD/GD2 + * + * This function will resize or crop + * + * @access public + * @param string + * @return bool + */ + function image_process_gd($action = 'resize') + { + $v2_override = FALSE; + + // If the target width/height match the source, AND if the new file name is not equal to the old file name + // we'll simply make a copy of the original with the new name... assuming dynamic rendering is off. + if ($this->dynamic_output === FALSE) + { + if ($this->orig_width == $this->width AND $this->orig_height == $this->height) + { + if ($this->source_image != $this->new_image) + { + if (@copy($this->full_src_path, $this->full_dst_path)) + { + @chmod($this->full_dst_path, DIR_WRITE_MODE); + } + } + + return TRUE; + } + } + + // Let's set up our values based on the action + if ($action == 'crop') + { + // Reassign the source width/height if cropping + $this->orig_width = $this->width; + $this->orig_height = $this->height; + + // GD 2.0 has a cropping bug so we'll test for it + if ($this->gd_version() !== FALSE) + { + $gd_version = str_replace('0', '', $this->gd_version()); + $v2_override = ($gd_version == 2) ? TRUE : FALSE; + } + } + else + { + // If resizing the x/y axis must be zero + $this->x_axis = 0; + $this->y_axis = 0; + } + + // Create the image handle + if ( ! ($src_img = $this->image_create_gd())) + { + return FALSE; + } + + // Create The Image + // + // old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater" + // it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment + // below should that ever prove inaccurate. + // + // if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor') AND $v2_override == FALSE) + if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor')) + { + $create = 'imagecreatetruecolor'; + $copy = 'imagecopyresampled'; + } + else + { + $create = 'imagecreate'; + $copy = 'imagecopyresized'; + } + + $dst_img = $create($this->width, $this->height); + $copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height); + + // Show the image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($dst_img); + } + else + { + // Or save it + if ( ! $this->image_save_gd($dst_img)) + { + return FALSE; + } + } + + // Kill the file handles + imagedestroy($dst_img); + imagedestroy($src_img); + + // Set the file to 777 + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Process Using ImageMagick + * + * This function will resize, crop or rotate + * + * @access public + * @param string + * @return bool + */ + function image_process_imagemagick($action = 'resize') + { + // Do we have a vaild library path? + if ($this->library_path == '') + { + $this->set_error('imglib_libpath_invalid'); + return FALSE; + } + + if ( ! preg_match("/convert$/i", $this->library_path)) + { + $this->library_path = rtrim($this->library_path, '/').'/'; + + $this->library_path .= 'convert'; + } + + // Execute the command + $cmd = $this->library_path." -quality ".$this->quality; + + if ($action == 'crop') + { + $cmd .= " -crop ".$this->width."x".$this->height."+".$this->x_axis."+".$this->y_axis." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1"; + } + elseif ($action == 'rotate') + { + switch ($this->rotation_angle) + { + case 'hor' : $angle = '-flop'; + break; + case 'vrt' : $angle = '-flip'; + break; + default : $angle = '-rotate '.$this->rotation_angle; + break; + } + + $cmd .= " ".$angle." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1"; + } + else // Resize + { + $cmd .= " -resize ".$this->width."x".$this->height." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1"; + } + + $retval = 1; + + @exec($cmd, $output, $retval); + + // Did it work? + if ($retval > 0) + { + $this->set_error('imglib_image_process_failed'); + return FALSE; + } + + // Set the file to 777 + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Process Using NetPBM + * + * This function will resize, crop or rotate + * + * @access public + * @param string + * @return bool + */ + function image_process_netpbm($action = 'resize') + { + if ($this->library_path == '') + { + $this->set_error('imglib_libpath_invalid'); + return FALSE; + } + + // Build the resizing command + switch ($this->image_type) + { + case 1 : + $cmd_in = 'giftopnm'; + $cmd_out = 'ppmtogif'; + break; + case 2 : + $cmd_in = 'jpegtopnm'; + $cmd_out = 'ppmtojpeg'; + break; + case 3 : + $cmd_in = 'pngtopnm'; + $cmd_out = 'ppmtopng'; + break; + } + + if ($action == 'crop') + { + $cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height; + } + elseif ($action == 'rotate') + { + switch ($this->rotation_angle) + { + case 90 : $angle = 'r270'; + break; + case 180 : $angle = 'r180'; + break; + case 270 : $angle = 'r90'; + break; + case 'vrt' : $angle = 'tb'; + break; + case 'hor' : $angle = 'lr'; + break; + } + + $cmd_inner = 'pnmflip -'.$angle.' '; + } + else // Resize + { + $cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height; + } + + $cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp'; + + $retval = 1; + + @exec($cmd, $output, $retval); + + // Did it work? + if ($retval > 0) + { + $this->set_error('imglib_image_process_failed'); + return FALSE; + } + + // With NetPBM we have to create a temporary image. + // If you try manipulating the original it fails so + // we have to rename the temp file. + copy ($this->dest_folder.'netpbm.tmp', $this->full_dst_path); + unlink ($this->dest_folder.'netpbm.tmp'); + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Rotate Using GD + * + * @access public + * @return bool + */ + function image_rotate_gd() + { + // Is Image Rotation Supported? + // this function is only supported as of PHP 4.3 + if ( ! function_exists('imagerotate')) + { + $this->set_error('imglib_rotate_unsupported'); + return FALSE; + } + + // Create the image handle + if ( ! ($src_img = $this->image_create_gd())) + { + return FALSE; + } + + // Set the background color + // This won't work with transparent PNG files so we are + // going to have to figure out how to determine the color + // of the alpha channel in a future release. + + $white = imagecolorallocate($src_img, 255, 255, 255); + + // Rotate it! + $dst_img = imagerotate($src_img, $this->rotation_angle, $white); + + // Save the Image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($dst_img); + } + else + { + // Or save it + if ( ! $this->image_save_gd($dst_img)) + { + return FALSE; + } + } + + // Kill the file handles + imagedestroy($dst_img); + imagedestroy($src_img); + + // Set the file to 777 + + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return true; + } + + // -------------------------------------------------------------------- + + /** + * Create Mirror Image using GD + * + * This function will flip horizontal or vertical + * + * @access public + * @return bool + */ + function image_mirror_gd() + { + if ( ! $src_img = $this->image_create_gd()) + { + return FALSE; + } + + $width = $this->orig_width; + $height = $this->orig_height; + + if ($this->rotation_angle == 'hor') + { + for ($i = 0; $i < $height; $i++) + { + $left = 0; + $right = $width-1; + + while ($left < $right) + { + $cl = imagecolorat($src_img, $left, $i); + $cr = imagecolorat($src_img, $right, $i); + + imagesetpixel($src_img, $left, $i, $cr); + imagesetpixel($src_img, $right, $i, $cl); + + $left++; + $right--; + } + } + } + else + { + for ($i = 0; $i < $width; $i++) + { + $top = 0; + $bot = $height-1; + + while ($top < $bot) + { + $ct = imagecolorat($src_img, $i, $top); + $cb = imagecolorat($src_img, $i, $bot); + + imagesetpixel($src_img, $i, $top, $cb); + imagesetpixel($src_img, $i, $bot, $ct); + + $top++; + $bot--; + } + } + } + + // Show the image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($src_img); + } + else + { + // Or save it + if ( ! $this->image_save_gd($src_img)) + { + return FALSE; + } + } + + // Kill the file handles + imagedestroy($src_img); + + // Set the file to 777 + @chmod($this->full_dst_path, DIR_WRITE_MODE); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Image Watermark + * + * This is a wrapper function that chooses the type + * of watermarking based on the specified preference. + * + * @access public + * @param string + * @return bool + */ + function watermark() + { + if ($this->wm_type == 'overlay') + { + return $this->overlay_watermark(); + } + else + { + return $this->text_watermark(); + } + } + + // -------------------------------------------------------------------- + + /** + * Watermark - Graphic Version + * + * @access public + * @return bool + */ + function overlay_watermark() + { + if ( ! function_exists('imagecolortransparent')) + { + $this->set_error('imglib_gd_required'); + return FALSE; + } + + // Fetch source image properties + $this->get_image_properties(); + + // Fetch watermark image properties + $props = $this->get_image_properties($this->wm_overlay_path, TRUE); + $wm_img_type = $props['image_type']; + $wm_width = $props['width']; + $wm_height = $props['height']; + + // Create two image resources + $wm_img = $this->image_create_gd($this->wm_overlay_path, $wm_img_type); + $src_img = $this->image_create_gd($this->full_src_path); + + // Reverse the offset if necessary + // When the image is positioned at the bottom + // we don't want the vertical offset to push it + // further down. We want the reverse, so we'll + // invert the offset. Same with the horizontal + // offset when the image is at the right + + $this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1)); + $this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1)); + + if ($this->wm_vrt_alignment == 'B') + $this->wm_vrt_offset = $this->wm_vrt_offset * -1; + + if ($this->wm_hor_alignment == 'R') + $this->wm_hor_offset = $this->wm_hor_offset * -1; + + // Set the base x and y axis values + $x_axis = $this->wm_hor_offset + $this->wm_padding; + $y_axis = $this->wm_vrt_offset + $this->wm_padding; + + // Set the vertical position + switch ($this->wm_vrt_alignment) + { + case 'T': + break; + case 'M': $y_axis += ($this->orig_height / 2) - ($wm_height / 2); + break; + case 'B': $y_axis += $this->orig_height - $wm_height; + break; + } + + // Set the horizontal position + switch ($this->wm_hor_alignment) + { + case 'L': + break; + case 'C': $x_axis += ($this->orig_width / 2) - ($wm_width / 2); + break; + case 'R': $x_axis += $this->orig_width - $wm_width; + break; + } + + // Build the finalized image + if ($wm_img_type == 3 AND function_exists('imagealphablending')) + { + @imagealphablending($src_img, TRUE); + } + + // Set RGB values for text and shadow + $rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp); + $alpha = ($rgba & 0x7F000000) >> 24; + + // make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency + if ($alpha > 0) + { + // copy the image directly, the image's alpha transparency being the sole determinant of blending + imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height); + } + else + { + // set our RGB value from above to be transparent and merge the images with the specified opacity + imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp)); + imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity); + } + + // Output the image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($src_img); + } + else + { + if ( ! $this->image_save_gd($src_img)) + { + return FALSE; + } + } + + imagedestroy($src_img); + imagedestroy($wm_img); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Watermark - Text Version + * + * @access public + * @return bool + */ + function text_watermark() + { + if ( ! ($src_img = $this->image_create_gd())) + { + return FALSE; + } + + if ($this->wm_use_truetype == TRUE AND ! file_exists($this->wm_font_path)) + { + $this->set_error('imglib_missing_font'); + return FALSE; + } + + // Fetch source image properties + $this->get_image_properties(); + + // Set RGB values for text and shadow + $this->wm_font_color = str_replace('#', '', $this->wm_font_color); + $this->wm_shadow_color = str_replace('#', '', $this->wm_shadow_color); + + $R1 = hexdec(substr($this->wm_font_color, 0, 2)); + $G1 = hexdec(substr($this->wm_font_color, 2, 2)); + $B1 = hexdec(substr($this->wm_font_color, 4, 2)); + + $R2 = hexdec(substr($this->wm_shadow_color, 0, 2)); + $G2 = hexdec(substr($this->wm_shadow_color, 2, 2)); + $B2 = hexdec(substr($this->wm_shadow_color, 4, 2)); + + $txt_color = imagecolorclosest($src_img, $R1, $G1, $B1); + $drp_color = imagecolorclosest($src_img, $R2, $G2, $B2); + + // Reverse the vertical offset + // When the image is positioned at the bottom + // we don't want the vertical offset to push it + // further down. We want the reverse, so we'll + // invert the offset. Note: The horizontal + // offset flips itself automatically + + if ($this->wm_vrt_alignment == 'B') + $this->wm_vrt_offset = $this->wm_vrt_offset * -1; + + if ($this->wm_hor_alignment == 'R') + $this->wm_hor_offset = $this->wm_hor_offset * -1; + + // Set font width and height + // These are calculated differently depending on + // whether we are using the true type font or not + if ($this->wm_use_truetype == TRUE) + { + if ($this->wm_font_size == '') + $this->wm_font_size = '17'; + + $fontwidth = $this->wm_font_size-($this->wm_font_size/4); + $fontheight = $this->wm_font_size; + $this->wm_vrt_offset += $this->wm_font_size; + } + else + { + $fontwidth = imagefontwidth($this->wm_font_size); + $fontheight = imagefontheight($this->wm_font_size); + } + + // Set base X and Y axis values + $x_axis = $this->wm_hor_offset + $this->wm_padding; + $y_axis = $this->wm_vrt_offset + $this->wm_padding; + + // Set verticle alignment + if ($this->wm_use_drop_shadow == FALSE) + $this->wm_shadow_distance = 0; + + $this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1)); + $this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1)); + + switch ($this->wm_vrt_alignment) + { + case "T" : + break; + case "M": $y_axis += ($this->orig_height/2)+($fontheight/2); + break; + case "B": $y_axis += ($this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight/2)); + break; + } + + $x_shad = $x_axis + $this->wm_shadow_distance; + $y_shad = $y_axis + $this->wm_shadow_distance; + + // Set horizontal alignment + switch ($this->wm_hor_alignment) + { + case "L": + break; + case "R": + if ($this->wm_use_drop_shadow) + $x_shad += ($this->orig_width - $fontwidth*strlen($this->wm_text)); + $x_axis += ($this->orig_width - $fontwidth*strlen($this->wm_text)); + break; + case "C": + if ($this->wm_use_drop_shadow) + $x_shad += floor(($this->orig_width - $fontwidth*strlen($this->wm_text))/2); + $x_axis += floor(($this->orig_width -$fontwidth*strlen($this->wm_text))/2); + break; + } + + // Add the text to the source image + if ($this->wm_use_truetype) + { + if ($this->wm_use_drop_shadow) + imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text); + imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text); + } + else + { + if ($this->wm_use_drop_shadow) + imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color); + imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color); + } + + // Output the final image + if ($this->dynamic_output == TRUE) + { + $this->image_display_gd($src_img); + } + else + { + $this->image_save_gd($src_img); + } + + imagedestroy($src_img); + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Create Image - GD + * + * This simply creates an image resource handle + * based on the type of image being processed + * + * @access public + * @param string + * @return resource + */ + function image_create_gd($path = '', $image_type = '') + { + if ($path == '') + $path = $this->full_src_path; + + if ($image_type == '') + $image_type = $this->image_type; + + + switch ($image_type) + { + case 1 : + if ( ! function_exists('imagecreatefromgif')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); + return FALSE; + } + + return imagecreatefromgif($path); + break; + case 2 : + if ( ! function_exists('imagecreatefromjpeg')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); + return FALSE; + } + + return imagecreatefromjpeg($path); + break; + case 3 : + if ( ! function_exists('imagecreatefrompng')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); + return FALSE; + } + + return imagecreatefrompng($path); + break; + + } + + $this->set_error(array('imglib_unsupported_imagecreate')); + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Write image file to disk - GD + * + * Takes an image resource as input and writes the file + * to the specified destination + * + * @access public + * @param resource + * @return bool + */ + function image_save_gd($resource) + { + switch ($this->image_type) + { + case 1 : + if ( ! function_exists('imagegif')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); + return FALSE; + } + + if ( ! @imagegif($resource, $this->full_dst_path)) + { + $this->set_error('imglib_save_failed'); + return FALSE; + } + break; + case 2 : + if ( ! function_exists('imagejpeg')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); + return FALSE; + } + + if (phpversion() == '4.4.1') + { + @touch($this->full_dst_path); // PHP 4.4.1 bug #35060 - workaround + } + + if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality)) + { + $this->set_error('imglib_save_failed'); + return FALSE; + } + break; + case 3 : + if ( ! function_exists('imagepng')) + { + $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); + return FALSE; + } + + if ( ! @imagepng($resource, $this->full_dst_path)) + { + $this->set_error('imglib_save_failed'); + return FALSE; + } + break; + default : + $this->set_error(array('imglib_unsupported_imagecreate')); + return FALSE; + break; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Dynamically outputs an image + * + * @access public + * @param resource + * @return void + */ + function image_display_gd($resource) + { + header("Content-Disposition: filename={$this->source_image};"); + header("Content-Type: {$this->mime_type}"); + header('Content-Transfer-Encoding: binary'); + header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT'); + + switch ($this->image_type) + { + case 1 : imagegif($resource); + break; + case 2 : imagejpeg($resource, '', $this->quality); + break; + case 3 : imagepng($resource); + break; + default : echo 'Unable to display the image'; + break; + } + } + + // -------------------------------------------------------------------- + + /** + * Re-proportion Image Width/Height + * + * When creating thumbs, the desired width/height + * can end up warping the image due to an incorrect + * ratio between the full-sized image and the thumb. + * + * This function lets us re-proportion the width/height + * if users choose to maintain the aspect ratio when resizing. + * + * @access public + * @return void + */ + function image_reproportion() + { + if ( ! is_numeric($this->width) OR ! is_numeric($this->height) OR $this->width == 0 OR $this->height == 0) + return; + + if ( ! is_numeric($this->orig_width) OR ! is_numeric($this->orig_height) OR $this->orig_width == 0 OR $this->orig_height == 0) + return; + + $new_width = ceil($this->orig_width*$this->height/$this->orig_height); + $new_height = ceil($this->width*$this->orig_height/$this->orig_width); + + $ratio = (($this->orig_height/$this->orig_width) - ($this->height/$this->width)); + + if ($this->master_dim != 'width' AND $this->master_dim != 'height') + { + $this->master_dim = ($ratio < 0) ? 'width' : 'height'; + } + + if (($this->width != $new_width) AND ($this->height != $new_height)) + { + if ($this->master_dim == 'height') + { + $this->width = $new_width; + } + else + { + $this->height = $new_height; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Get image properties + * + * A helper function that gets info about the file + * + * @access public + * @param string + * @return mixed + */ + function get_image_properties($path = '', $return = FALSE) + { + // For now we require GD but we should + // find a way to determine this using IM or NetPBM + + if ($path == '') + $path = $this->full_src_path; + + if ( ! file_exists($path)) + { + $this->set_error('imglib_invalid_path'); + return FALSE; + } + + $vals = @getimagesize($path); + + $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); + + $mime = (isset($types[$vals['2']])) ? 'image/'.$types[$vals['2']] : 'image/jpg'; + + if ($return == TRUE) + { + $v['width'] = $vals['0']; + $v['height'] = $vals['1']; + $v['image_type'] = $vals['2']; + $v['size_str'] = $vals['3']; + $v['mime_type'] = $mime; + + return $v; + } + + $this->orig_width = $vals['0']; + $this->orig_height = $vals['1']; + $this->image_type = $vals['2']; + $this->size_str = $vals['3']; + $this->mime_type = $mime; + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Size calculator + * + * This function takes a known width x height and + * recalculates it to a new size. Only one + * new variable needs to be known + * + * $props = array( + * 'width' => $width, + * 'height' => $height, + * 'new_width' => 40, + * 'new_height' => '' + * ); + * + * @access public + * @param array + * @return array + */ + function size_calculator($vals) + { + if ( ! is_array($vals)) + { + return; + } + + $allowed = array('new_width', 'new_height', 'width', 'height'); + + foreach ($allowed as $item) + { + if ( ! isset($vals[$item]) OR $vals[$item] == '') + $vals[$item] = 0; + } + + if ($vals['width'] == 0 OR $vals['height'] == 0) + { + return $vals; + } + + if ($vals['new_width'] == 0) + { + $vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']); + } + elseif ($vals['new_height'] == 0) + { + $vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']); + } + + return $vals; + } + + // -------------------------------------------------------------------- + + /** + * Explode source_image + * + * This is a helper function that extracts the extension + * from the source_image. This function lets us deal with + * source_images with multiple periods, like: my.cool.jpg + * It returns an associative array with two elements: + * $array['ext'] = '.jpg'; + * $array['name'] = 'my.cool'; + * + * @access public + * @param array + * @return array + */ + function explode_name($source_image) + { + $ext = strrchr($source_image, '.'); + $name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext)); + + return array('ext' => $ext, 'name' => $name); + } + + // -------------------------------------------------------------------- + + /** + * Is GD Installed? + * + * @access public + * @return bool + */ + function gd_loaded() + { + if ( ! extension_loaded('gd')) + { + if ( ! dl('gd.so')) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Get GD version + * + * @access public + * @return mixed + */ + function gd_version() + { + if (function_exists('gd_info')) + { + $gd_version = @gd_info(); + $gd_version = preg_replace("/\D/", "", $gd_version['GD Version']); + + return $gd_version; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Set error message + * + * @access public + * @param string + * @return void + */ + function set_error($msg) + { + $CI =& get_instance(); + $CI->lang->load('imglib'); + + if (is_array($msg)) + { + foreach ($msg as $val) + { + + $msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val); + $this->error_msg[] = $msg; + log_message('error', $msg); + } + } + else + { + $msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg); + $this->error_msg[] = $msg; + log_message('error', $msg); + } + } + + // -------------------------------------------------------------------- + + /** + * Show error messages + * + * @access public + * @param string + * @return string + */ + function display_errors($open = '', $close = '
') + { + $str = ''; + foreach ($this->error_msg as $val) + { + $str .= $open.$val.$close; + } + + return $str; + } + +} +// END Image_lib Class + +/* End of file Image_lib.php */ +/* Location: ./system/libraries/Image_lib.php */ \ No newline at end of file diff --git a/system/libraries/Input.php b/system/libraries/Input.php new file mode 100644 index 000000000..e7bf72779 --- /dev/null +++ b/system/libraries/Input.php @@ -0,0 +1,1067 @@ + '[removed]', + 'document.write' => '[removed]', + '.parentNode' => '[removed]', + '.innerHTML' => '[removed]', + 'window.location' => '[removed]', + '-moz-binding' => '[removed]', + '' => '-->', + ' '<![CDATA[' + ); + /* never allowed, regex replacement */ + var $never_allowed_regex = array( + "javascript\s*:" => '[removed]', + "expression\s*(\(|&\#40;)" => '[removed]', // CSS and IE + "vbscript\s*:" => '[removed]', // IE, surprise! + "Redirect\s+302" => '[removed]' + ); + + /** + * Constructor + * + * Sets whether to globally enable the XSS processing + * and whether to allow the $_GET array + * + * @access public + */ + function CI_Input() + { + log_message('debug', "Input Class Initialized"); + + $CFG =& load_class('Config'); + $this->use_xss_clean = ($CFG->item('global_xss_filtering') === TRUE) ? TRUE : FALSE; + $this->allow_get_array = ($CFG->item('enable_query_strings') === TRUE) ? TRUE : FALSE; + $this->_sanitize_globals(); + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Globals + * + * This function does the following: + * + * Unsets $_GET data (if query strings are not enabled) + * + * Unsets all globals if register_globals is enabled + * + * Standardizes newline characters to \n + * + * @access private + * @return void + */ + function _sanitize_globals() + { + // Would kind of be "wrong" to unset any of these GLOBALS + $protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST', '_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA', + 'system_folder', 'application_folder', 'BM', 'EXT', 'CFG', 'URI', 'RTR', 'OUT', 'IN'); + + // Unset globals for security. + // This is effectively the same as register_globals = off + foreach (array($_GET, $_POST, $_COOKIE, $_SERVER, $_FILES, $_ENV, (isset($_SESSION) && is_array($_SESSION)) ? $_SESSION : array()) as $global) + { + if ( ! is_array($global)) + { + if ( ! in_array($global, $protected)) + { + unset($GLOBALS[$global]); + } + } + else + { + foreach ($global as $key => $val) + { + if ( ! in_array($key, $protected)) + { + unset($GLOBALS[$key]); + } + + if (is_array($val)) + { + foreach($val as $k => $v) + { + if ( ! in_array($k, $protected)) + { + unset($GLOBALS[$k]); + } + } + } + } + } + } + + // Is $_GET data allowed? If not we'll set the $_GET to an empty array + if ($this->allow_get_array == FALSE) + { + $_GET = array(); + } + else + { + $_GET = $this->_clean_input_data($_GET); + } + + // Clean $_POST Data + $_POST = $this->_clean_input_data($_POST); + + // Clean $_COOKIE Data + // Also get rid of specially treated cookies that might be set by a server + // or silly application, that are of no use to a CI application anyway + // but that when present will trip our 'Disallowed Key Characters' alarm + // http://www.ietf.org/rfc/rfc2109.txt + // note that the key names below are single quoted strings, and are not PHP variables + unset($_COOKIE['$Version']); + unset($_COOKIE['$Path']); + unset($_COOKIE['$Domain']); + $_COOKIE = $this->_clean_input_data($_COOKIE); + + log_message('debug', "Global POST and COOKIE data sanitized"); + } + + // -------------------------------------------------------------------- + + /** + * Clean Input Data + * + * This is a helper function. It escapes data and + * standardizes newline characters to \n + * + * @access private + * @param string + * @return string + */ + function _clean_input_data($str) + { + if (is_array($str)) + { + $new_array = array(); + foreach ($str as $key => $val) + { + $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + } + return $new_array; + } + + // We strip slashes if magic quotes is on to keep things consistent + if (get_magic_quotes_gpc()) + { + $str = stripslashes($str); + } + + // Should we filter the input data? + if ($this->use_xss_clean === TRUE) + { + $str = $this->xss_clean($str); + } + + // Standardize newlines + if (strpos($str, "\r") !== FALSE) + { + $str = str_replace(array("\r\n", "\r"), "\n", $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Clean Keys + * + * This is a helper function. To prevent malicious users + * from trying to exploit keys we make sure that keys are + * only named with alpha-numeric text and a few other items. + * + * @access private + * @param string + * @return string + */ + function _clean_input_keys($str) + { + if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str)) + { + exit('Disallowed Key Characters.'); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Fetch from array + * + * This is a helper function to retrieve values from global arrays + * + * @access private + * @param array + * @param string + * @param bool + * @return string + */ + function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE) + { + if ( ! isset($array[$index])) + { + return FALSE; + } + + if ($xss_clean === TRUE) + { + return $this->xss_clean($array[$index]); + } + + return $array[$index]; + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the GET array + * + * @access public + * @param string + * @param bool + * @return string + */ + function get($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_GET, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the POST array + * + * @access public + * @param string + * @param bool + * @return string + */ + function post($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_POST, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from either the GET array or the POST + * + * @access public + * @param string The index key + * @param bool XSS cleaning + * @return string + */ + function get_post($index = '', $xss_clean = FALSE) + { + if ( ! isset($_POST[$index]) ) + { + return $this->get($index, $xss_clean); + } + else + { + return $this->post($index, $xss_clean); + } + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the COOKIE array + * + * @access public + * @param string + * @param bool + * @return string + */ + function cookie($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch an item from the SERVER array + * + * @access public + * @param string + * @param bool + * @return string + */ + function server($index = '', $xss_clean = FALSE) + { + return $this->_fetch_from_array($_SERVER, $index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** + * Fetch the IP Address + * + * @access public + * @return string + */ + function ip_address() + { + if ($this->ip_address !== FALSE) + { + return $this->ip_address; + } + + if (config_item('proxy_ips') != '' && $this->server('HTTP_X_FORWARDED_FOR') && $this->server('REMOTE_ADDR')) + { + $proxies = preg_split('/[\s,]/', config_item('proxy_ips'), -1, PREG_SPLIT_NO_EMPTY); + $proxies = is_array($proxies) ? $proxies : array($proxies); + + $this->ip_address = in_array($_SERVER['REMOTE_ADDR'], $proxies) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']; + } + elseif ($this->server('REMOTE_ADDR') AND $this->server('HTTP_CLIENT_IP')) + { + $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; + } + elseif ($this->server('REMOTE_ADDR')) + { + $this->ip_address = $_SERVER['REMOTE_ADDR']; + } + elseif ($this->server('HTTP_CLIENT_IP')) + { + $this->ip_address = $_SERVER['HTTP_CLIENT_IP']; + } + elseif ($this->server('HTTP_X_FORWARDED_FOR')) + { + $this->ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; + } + + if ($this->ip_address === FALSE) + { + $this->ip_address = '0.0.0.0'; + return $this->ip_address; + } + + if (strstr($this->ip_address, ',')) + { + $x = explode(',', $this->ip_address); + $this->ip_address = trim(end($x)); + } + + if ( ! $this->valid_ip($this->ip_address)) + { + $this->ip_address = '0.0.0.0'; + } + + return $this->ip_address; + } + + // -------------------------------------------------------------------- + + /** + * Validate IP Address + * + * Updated version suggested by Geert De Deckere + * + * @access public + * @param string + * @return string + */ + function valid_ip($ip) + { + $ip_segments = explode('.', $ip); + + // Always 4 segments needed + if (count($ip_segments) != 4) + { + return FALSE; + } + // IP can not start with 0 + if ($ip_segments[0][0] == '0') + { + return FALSE; + } + // Check each segment + foreach ($ip_segments as $segment) + { + // IP segments must be digits and can not be + // longer than 3 digits or greater then 255 + if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * User Agent + * + * @access public + * @return string + */ + function user_agent() + { + if ($this->user_agent !== FALSE) + { + return $this->user_agent; + } + + $this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT']; + + return $this->user_agent; + } + + // -------------------------------------------------------------------- + + /** + * Filename Security + * + * @access public + * @param string + * @return string + */ + function filename_security($str) + { + $bad = array( + "../", + "./", + "", + "<", + ">", + "'", + '"', + '&', + '$', + '#', + '{', + '}', + '[', + ']', + '=', + ';', + '?', + "%20", + "%22", + "%3c", // < + "%253c", // < + "%3e", // > + "%0e", // > + "%28", // ( + "%29", // ) + "%2528", // ( + "%26", // & + "%24", // $ + "%3f", // ? + "%3b", // ; + "%3d" // = + ); + + return stripslashes(str_replace($bad, '', $str)); + } + + // -------------------------------------------------------------------- + + /** + * XSS Clean + * + * Sanitizes data so that Cross Site Scripting Hacks can be + * prevented. This function does a fair amount of work but + * it is extremely thorough, designed to prevent even the + * most obscure XSS attempts. Nothing is ever 100% foolproof, + * of course, but I haven't been able to get anything passed + * the filter. + * + * Note: This function should only be used to deal with data + * upon submission. It's not something that should + * be used for general runtime processing. + * + * This function was based in part on some code and ideas I + * got from Bitflux: http://blog.bitflux.ch/wiki/XSS_Prevention + * + * To help develop this script I used this great list of + * vulnerabilities along with a few other hacks I've + * harvested from examining vulnerabilities in other programs: + * http://ha.ckers.org/xss.html + * + * @access public + * @param string + * @return string + */ + function xss_clean($str, $is_image = FALSE) + { + /* + * Is the string an array? + * + */ + if (is_array($str)) + { + while (list($key) = each($str)) + { + $str[$key] = $this->xss_clean($str[$key]); + } + + return $str; + } + + /* + * Remove Invisible Characters + */ + $str = $this->_remove_invisible_characters($str); + + /* + * Protect GET variables in URLs + */ + + // 901119URL5918AMP18930PROTECT8198 + + $str = preg_replace('|\&([a-z\_0-9]+)\=([a-z\_0-9]+)|i', $this->xss_hash()."\\1=\\2", $str); + + /* + * Validate standard character entities + * + * Add a semicolon if missing. We do this to enable + * the conversion of entities to ASCII later. + * + */ + $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str); + + /* + * Validate UTF16 two byte encoding (x00) + * + * Just as above, adds a semicolon if missing. + * + */ + $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str); + + /* + * Un-Protect GET variables in URLs + */ + $str = str_replace($this->xss_hash(), '&', $str); + + /* + * URL Decode + * + * Just in case stuff like this is submitted: + * + * Google + * + * Note: Use rawurldecode() so it does not remove plus signs + * + */ + $str = rawurldecode($str); + + /* + * Convert character entities to ASCII + * + * This permits our tests below to work reliably. + * We only convert entities that are within tags since + * these are the ones that will pose security problems. + * + */ + + $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); + + $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", array($this, '_html_entity_decode_callback'), $str); + + /* + * Remove Invisible Characters Again! + */ + $str = $this->_remove_invisible_characters($str); + + /* + * Convert all tabs to spaces + * + * This prevents strings like this: ja vascript + * NOTE: we deal with spaces between characters later. + * NOTE: preg_replace was found to be amazingly slow here on large blocks of data, + * so we use str_replace. + * + */ + + if (strpos($str, "\t") !== FALSE) + { + $str = str_replace("\t", ' ', $str); + } + + /* + * Capture converted string for later comparison + */ + $converted_string = $str; + + /* + * Not Allowed Under Any Conditions + */ + + foreach ($this->never_allowed_str as $key => $val) + { + $str = str_replace($key, $val, $str); + } + + foreach ($this->never_allowed_regex as $key => $val) + { + $str = preg_replace("#".$key."#i", $val, $str); + } + + /* + * Makes PHP tags safe + * + * Note: XML tags are inadvertently replaced too: + * + * '), array('<?', '?>'), $str); + } + + /* + * Compact any exploded words + * + * This corrects words like: j a v a s c r i p t + * These words are compacted back to their correct state. + * + */ + $words = array('javascript', 'expression', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window'); + foreach ($words as $word) + { + $temp = ''; + + for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++) + { + $temp .= substr($word, $i, 1)."\s*"; + } + + // We only want to do this when it is followed by a non-word character + // That way valid stuff like "dealer to" does not become "dealerto" + $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', array($this, '_compact_exploded_words'), $str); + } + + /* + * Remove disallowed Javascript in links or img tags + * We used to do some version comparisons and use of stripos for PHP5, but it is dog slow compared + * to these simplified non-capturing preg_match(), especially if the pattern exists in the string + */ + do + { + $original = $str; + + if (preg_match("/]*?)(>|$)#si", array($this, '_js_link_removal'), $str); + } + + if (preg_match("/