a grand renaming so that the most significant portion of the name comes first

This commit is contained in:
Dan Buch
2012-03-03 21:45:20 -05:00
parent c8b8078175
commit f4f448926d
1300 changed files with 0 additions and 234 deletions

View File

@@ -0,0 +1,36 @@
<?php
/**
* Application level Controller
*
* This file is application-wide controller file. You can put all
* application-wide controller-related methods here.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* This is a placeholder class.
* Create the same file in app/app_controller.php
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* @package cake
* @subpackage cake.cake.libs.controller
* @link http://book.cakephp.org/view/957/The-App-Controller
*/
class AppController extends Controller {
}

View File

@@ -0,0 +1,263 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since CakePHP(tm) v TBD
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Handler for Controller::$components
*
* @package cake
* @subpackage cake.cake.libs.controller
* @link http://book.cakephp.org/view/993/Components
*/
class Component extends Object {
/**
* Contains various controller variable information (plugin, name, base).
*
* @var object
* @access private
*/
var $__controllerVars = array('plugin' => null, 'name' => null, 'base' => null);
/**
* List of loaded components.
*
* @var object
* @access protected
*/
var $_loaded = array();
/**
* List of components attached directly to the controller, which callbacks
* should be executed on.
*
* @var object
* @access protected
*/
var $_primary = array();
/**
* Settings for loaded components.
*
* @var array
* @access private
*/
var $__settings = array();
/**
* Used to initialize the components for current controller.
*
* @param object $controller Controller with components to load
* @return void
* @access public
*/
function init(&$controller) {
if (!is_array($controller->components)) {
return;
}
$this->__controllerVars = array(
'plugin' => $controller->plugin, 'name' => $controller->name,
'base' => $controller->base
);
$this->_loadComponents($controller);
}
/**
* Called before the Controller::beforeFilter().
*
* @param object $controller Controller with components to initialize
* @return void
* @access public
* @link http://book.cakephp.org/view/998/MVC-Class-Access-Within-Components
*/
function initialize(&$controller) {
foreach (array_keys($this->_loaded) as $name) {
$component =& $this->_loaded[$name];
if (method_exists($component,'initialize') && $component->enabled === true) {
$settings = array();
if (isset($this->__settings[$name])) {
$settings = $this->__settings[$name];
}
$component->initialize($controller, $settings);
}
}
}
/**
* Called after the Controller::beforeFilter() and before the controller action
*
* @param object $controller Controller with components to startup
* @return void
* @access public
* @link http://book.cakephp.org/view/998/MVC-Class-Access-Within-Components
* @deprecated See Component::triggerCallback()
*/
function startup(&$controller) {
$this->triggerCallback('startup', $controller);
}
/**
* Called after the Controller::beforeRender(), after the view class is loaded, and before the
* Controller::render()
*
* @param object $controller Controller with components to beforeRender
* @return void
* @access public
* @deprecated See Component::triggerCallback()
*/
function beforeRender(&$controller) {
$this->triggerCallback('beforeRender', $controller);
}
/**
* Called before Controller::redirect().
*
* @param object $controller Controller with components to beforeRedirect
* @return void
* @access public
*/
function beforeRedirect(&$controller, $url, $status = null, $exit = true) {
$response = array();
foreach ($this->_primary as $name) {
$component =& $this->_loaded[$name];
if ($component->enabled === true && method_exists($component, 'beforeRedirect')) {
$resp = $component->beforeRedirect($controller, $url, $status, $exit);
if ($resp === false) {
return false;
}
$response[] = $resp;
}
}
return $response;
}
/**
* Called after Controller::render() and before the output is printed to the browser.
*
* @param object $controller Controller with components to shutdown
* @return void
* @access public
* @deprecated See Component::triggerCallback()
*/
function shutdown(&$controller) {
$this->triggerCallback('shutdown', $controller);
}
/**
* Trigger a callback on all primary components. Will fire $callback on all components
* that have such a method. You can implement and fire custom callbacks in addition to the
* standard ones.
*
* example use, from inside a controller:
*
* `$this->Component->triggerCallback('beforeFilter', $this);`
*
* will trigger the beforeFilter callback on all components that have implemented one. You
* can trigger any method in this fashion.
*
* @param Controller $controller Controller instance
* @param string $callback Callback to trigger.
* @return void
* @access public
*/
function triggerCallback($callback, &$controller) {
foreach ($this->_primary as $name) {
$component =& $this->_loaded[$name];
if (method_exists($component, $callback) && $component->enabled === true) {
$component->{$callback}($controller);
}
}
}
/**
* Loads components used by this component.
*
* @param object $object Object with a Components array
* @param object $parent the parent of the current object
* @return void
* @access protected
*/
function _loadComponents(&$object, $parent = null) {
$base = $this->__controllerVars['base'];
$normal = Set::normalize($object->components);
foreach ((array)$normal as $component => $config) {
$plugin = isset($this->__controllerVars['plugin']) ? $this->__controllerVars['plugin'] . '.' : null;
list($plugin, $component) = pluginSplit($component, true, $plugin);
$componentCn = $component . 'Component';
if (!class_exists($componentCn)) {
if (is_null($plugin) || !App::import('Component', $plugin . $component)) {
if (!App::import('Component', $component)) {
$this->cakeError('missingComponentFile', array(array(
'className' => $this->__controllerVars['name'],
'component' => $component,
'file' => Inflector::underscore($component) . '.php',
'base' => $base,
'code' => 500
)));
return false;
}
}
if (!class_exists($componentCn)) {
$this->cakeError('missingComponentClass', array(array(
'className' => $this->__controllerVars['name'],
'component' => $component,
'file' => Inflector::underscore($component) . '.php',
'base' => $base,
'code' => 500
)));
return false;
}
}
if ($parent === null) {
$this->_primary[] = $component;
}
if (isset($this->_loaded[$component])) {
$object->{$component} =& $this->_loaded[$component];
if (!empty($config) && isset($this->__settings[$component])) {
$this->__settings[$component] = array_merge($this->__settings[$component], $config);
} elseif (!empty($config)) {
$this->__settings[$component] = $config;
}
} else {
if ($componentCn === 'SessionComponent') {
$object->{$component} =& new $componentCn($base);
} else {
$object->{$component} =& new $componentCn();
}
$object->{$component}->enabled = true;
$this->_loaded[$component] =& $object->{$component};
if (!empty($config)) {
$this->__settings[$component] = $config;
}
}
if (isset($object->{$component}->components) && is_array($object->{$component}->components) && (!isset($object->{$component}->{$parent}))) {
$this->_loadComponents($object->{$component}, $component);
}
}
}
}

View File

@@ -0,0 +1,638 @@
<?php
/**
* Access Control List factory class.
*
* Permissions system.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Access Control List factory class.
*
* Uses a strategy pattern to allow custom ACL implementations to be used with the same component interface.
* You can define by changing `Configure::write('Acl.classname', 'DbAcl');` in your core.php. Concrete ACL
* implementations should extend `AclBase` and implement the methods it defines.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1242/Access-Control-Lists
*/
class AclComponent extends Object {
/**
* Instance of an ACL class
*
* @var object
* @access protected
*/
var $_Instance = null;
/**
* Constructor. Will return an instance of the correct ACL class as defined in `Configure::read('Acl.classname')`
*
*/
function __construct() {
$name = Inflector::camelize(strtolower(Configure::read('Acl.classname')));
if (!class_exists($name)) {
if (App::import('Component', $name)) {
list($plugin, $name) = pluginSplit($name);
$name .= 'Component';
} else {
trigger_error(sprintf(__('Could not find %s.', true), $name), E_USER_WARNING);
}
}
$this->_Instance =& new $name();
$this->_Instance->initialize($this);
}
/**
* Startup is not used
*
* @param object $controller Controller using this component
* @return boolean Proceed with component usage (true), or fail (false)
* @access public
*/
function startup(&$controller) {
return true;
}
/**
* Empty class defintion, to be overridden in subclasses.
*
* @access protected
*/
function _initACL() {
}
/**
* Pass-thru function for ACL check instance. Check methods
* are used to check whether or not an ARO can access an ACO
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function check($aro, $aco, $action = "*") {
return $this->_Instance->check($aro, $aco, $action);
}
/**
* Pass-thru function for ACL allow instance. Allow methods
* are used to grant an ARO access to an ACO.
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function allow($aro, $aco, $action = "*") {
return $this->_Instance->allow($aro, $aco, $action);
}
/**
* Pass-thru function for ACL deny instance. Deny methods
* are used to remove permission from an ARO to access an ACO.
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function deny($aro, $aco, $action = "*") {
return $this->_Instance->deny($aro, $aco, $action);
}
/**
* Pass-thru function for ACL inherit instance. Inherit methods
* modify the permission for an ARO to be that of its parent object.
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function inherit($aro, $aco, $action = "*") {
return $this->_Instance->inherit($aro, $aco, $action);
}
/**
* Pass-thru function for ACL grant instance. An alias for AclComponent::allow()
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function grant($aro, $aco, $action = "*") {
return $this->_Instance->grant($aro, $aco, $action);
}
/**
* Pass-thru function for ACL grant instance. An alias for AclComponent::deny()
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function revoke($aro, $aco, $action = "*") {
return $this->_Instance->revoke($aro, $aco, $action);
}
}
/**
* Access Control List abstract class. Not to be instantiated.
* Subclasses of this class are used by AclComponent to perform ACL checks in Cake.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @abstract
*/
class AclBase extends Object {
/**
* This class should never be instantiated, just subclassed.
*
*/
function __construct() {
if (strcasecmp(get_class($this), "AclBase") == 0 || !is_subclass_of($this, "AclBase")) {
trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration.", true), E_USER_ERROR);
return NULL;
}
}
/**
* Empty method to be overridden in subclasses
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @access public
*/
function check($aro, $aco, $action = "*") {
}
/**
* Empty method to be overridden in subclasses
*
* @param object $component Component
* @access public
*/
function initialize(&$component) {
}
}
/**
* DbAcl implements an ACL control system in the database. ARO's and ACO's are
* structured into trees and a linking table is used to define permissions. You
* can install the schema for DbAcl with the Schema Shell.
*
* `$aco` and `$aro` parameters can be slash delimited paths to tree nodes.
*
* eg. `controllers/Users/edit`
*
* Would point to a tree structure like
*
* {{{
* controllers
* Users
* edit
* }}}
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class DbAcl extends AclBase {
/**
* Constructor
*
*/
function __construct() {
parent::__construct();
if (!class_exists('AclNode')) {
require LIBS . 'model' . DS . 'db_acl.php';
}
$this->Aro =& ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro'));
$this->Aco =& ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco'));
}
/**
* Initializes the containing component and sets the Aro/Aco objects to it.
*
* @param AclComponent $component
* @return void
* @access public
*/
function initialize(&$component) {
$component->Aro =& $this->Aro;
$component->Aco =& $this->Aco;
}
/**
* Checks if the given $aro has access to action $action in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success (true if ARO has access to action in ACO, false otherwise)
* @access public
* @link http://book.cakephp.org/view/1249/Checking-Permissions-The-ACL-Component
*/
function check($aro, $aco, $action = "*") {
if ($aro == null || $aco == null) {
return false;
}
$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
$aroPath = $this->Aro->node($aro);
$acoPath = $this->Aco->node($aco);
if (empty($aroPath) || empty($acoPath)) {
trigger_error(__("DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: ", true) . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
return false;
}
if ($acoPath == null || $acoPath == array()) {
trigger_error(__("DbAcl::check() - Failed ACO node lookup in permissions check. Node references:\nAro: ", true) . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
return false;
}
$aroNode = $aroPath[0];
$acoNode = $acoPath[0];
if ($action != '*' && !in_array('_' . $action, $permKeys)) {
trigger_error(sprintf(__("ACO permissions key %s does not exist in DbAcl::check()", true), $action), E_USER_NOTICE);
return false;
}
$inherited = array();
$acoIDs = Set::extract($acoPath, '{n}.' . $this->Aco->alias . '.id');
$count = count($aroPath);
for ($i = 0 ; $i < $count; $i++) {
$permAlias = $this->Aro->Permission->alias;
$perms = $this->Aro->Permission->find('all', array(
'conditions' => array(
"{$permAlias}.aro_id" => $aroPath[$i][$this->Aro->alias]['id'],
"{$permAlias}.aco_id" => $acoIDs
),
'order' => array($this->Aco->alias . '.lft' => 'desc'),
'recursive' => 0
));
if (empty($perms)) {
continue;
} else {
$perms = Set::extract($perms, '{n}.' . $this->Aro->Permission->alias);
foreach ($perms as $perm) {
if ($action == '*') {
foreach ($permKeys as $key) {
if (!empty($perm)) {
if ($perm[$key] == -1) {
return false;
} elseif ($perm[$key] == 1) {
$inherited[$key] = 1;
}
}
}
if (count($inherited) === count($permKeys)) {
return true;
}
} else {
switch ($perm['_' . $action]) {
case -1:
return false;
case 0:
continue;
break;
case 1:
return true;
break;
}
}
}
}
}
return false;
}
/**
* Allow $aro to have access to action $actions in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
* @return boolean Success
* @access public
* @link http://book.cakephp.org/view/1248/Assigning-Permissions
*/
function allow($aro, $aco, $actions = "*", $value = 1) {
$perms = $this->getAclLink($aro, $aco);
$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
$save = array();
if ($perms == false) {
trigger_error(__('DbAcl::allow() - Invalid node', true), E_USER_WARNING);
return false;
}
if (isset($perms[0])) {
$save = $perms[0][$this->Aro->Permission->alias];
}
if ($actions == "*") {
$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
$save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
} else {
if (!is_array($actions)) {
$actions = array('_' . $actions);
}
if (is_array($actions)) {
foreach ($actions as $action) {
if ($action{0} != '_') {
$action = '_' . $action;
}
if (in_array($action, $permKeys)) {
$save[$action] = $value;
}
}
}
}
list($save['aro_id'], $save['aco_id']) = array($perms['aro'], $perms['aco']);
if ($perms['link'] != null && !empty($perms['link'])) {
$save['id'] = $perms['link'][0][$this->Aro->Permission->alias]['id'];
} else {
unset($save['id']);
$this->Aro->Permission->id = null;
}
return ($this->Aro->Permission->save($save) !== false);
}
/**
* Deny access for $aro to action $action in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @access public
* @link http://book.cakephp.org/view/1248/Assigning-Permissions
*/
function deny($aro, $aco, $action = "*") {
return $this->allow($aro, $aco, $action, -1);
}
/**
* Let access for $aro to action $action in $aco be inherited
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @access public
*/
function inherit($aro, $aco, $action = "*") {
return $this->allow($aro, $aco, $action, 0);
}
/**
* Allow $aro to have access to action $actions in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @see allow()
* @access public
*/
function grant($aro, $aco, $action = "*") {
return $this->allow($aro, $aco, $action);
}
/**
* Deny access for $aro to action $action in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @see deny()
* @access public
*/
function revoke($aro, $aco, $action = "*") {
return $this->deny($aro, $aco, $action);
}
/**
* Get an array of access-control links between the given Aro and Aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @return array Indexed array with: 'aro', 'aco' and 'link'
* @access public
*/
function getAclLink($aro, $aco) {
$obj = array();
$obj['Aro'] = $this->Aro->node($aro);
$obj['Aco'] = $this->Aco->node($aco);
if (empty($obj['Aro']) || empty($obj['Aco'])) {
return false;
}
return array(
'aro' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
'aco' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id'),
'link' => $this->Aro->Permission->find('all', array('conditions' => array(
$this->Aro->Permission->alias . '.aro_id' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
$this->Aro->Permission->alias . '.aco_id' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id')
)))
);
}
/**
* Get the keys used in an ACO
*
* @param array $keys Permission model info
* @return array ACO keys
* @access protected
*/
function _getAcoKeys($keys) {
$newKeys = array();
$keys = array_keys($keys);
foreach ($keys as $key) {
if (!in_array($key, array('id', 'aro_id', 'aco_id'))) {
$newKeys[] = $key;
}
}
return $newKeys;
}
}
/**
* IniAcl implements an access control system using an INI file. An example
* of the ini file used can be found in /config/acl.ini.php.
*
* @package cake
* @subpackage cake.cake.libs.model.iniacl
*/
class IniAcl extends AclBase {
/**
* Array with configuration, parsed from ini file
*
* @var array
* @access public
*/
var $config = null;
/**
* The constructor must be overridden, as AclBase is abstract.
*
*/
function __construct() {
}
/**
* Main ACL check function. Checks to see if the ARO (access request object) has access to the
* ACO (access control object).Looks at the acl.ini.php file for permissions
* (see instructions in /config/acl.ini.php).
*
* @param string $aro ARO
* @param string $aco ACO
* @param string $aco_action Action
* @return boolean Success
* @access public
*/
function check($aro, $aco, $aco_action = null) {
if ($this->config == null) {
$this->config = $this->readConfigFile(CONFIGS . 'acl.ini.php');
}
$aclConfig = $this->config;
if (isset($aclConfig[$aro]['deny'])) {
$userDenies = $this->arrayTrim(explode(",", $aclConfig[$aro]['deny']));
if (array_search($aco, $userDenies)) {
return false;
}
}
if (isset($aclConfig[$aro]['allow'])) {
$userAllows = $this->arrayTrim(explode(",", $aclConfig[$aro]['allow']));
if (array_search($aco, $userAllows)) {
return true;
}
}
if (isset($aclConfig[$aro]['groups'])) {
$userGroups = $this->arrayTrim(explode(",", $aclConfig[$aro]['groups']));
foreach ($userGroups as $group) {
if (array_key_exists($group, $aclConfig)) {
if (isset($aclConfig[$group]['deny'])) {
$groupDenies=$this->arrayTrim(explode(",", $aclConfig[$group]['deny']));
if (array_search($aco, $groupDenies)) {
return false;
}
}
if (isset($aclConfig[$group]['allow'])) {
$groupAllows = $this->arrayTrim(explode(",", $aclConfig[$group]['allow']));
if (array_search($aco, $groupAllows)) {
return true;
}
}
}
}
}
return false;
}
/**
* Parses an INI file and returns an array that reflects the INI file's section structure. Double-quote friendly.
*
* @param string $fileName File
* @return array INI section structure
* @access public
*/
function readConfigFile($fileName) {
$fileLineArray = file($fileName);
foreach ($fileLineArray as $fileLine) {
$dataLine = trim($fileLine);
$firstChar = substr($dataLine, 0, 1);
if ($firstChar != ';' && $dataLine != '') {
if ($firstChar == '[' && substr($dataLine, -1, 1) == ']') {
$sectionName = preg_replace('/[\[\]]/', '', $dataLine);
} else {
$delimiter = strpos($dataLine, '=');
if ($delimiter > 0) {
$key = strtolower(trim(substr($dataLine, 0, $delimiter)));
$value = trim(substr($dataLine, $delimiter + 1));
if (substr($value, 0, 1) == '"' && substr($value, -1) == '"') {
$value = substr($value, 1, -1);
}
$iniSetting[$sectionName][$key]=stripcslashes($value);
} else {
if (!isset($sectionName)) {
$sectionName = '';
}
$iniSetting[$sectionName][strtolower(trim($dataLine))]='';
}
}
}
}
return $iniSetting;
}
/**
* Removes trailing spaces on all array elements (to prepare for searching)
*
* @param array $array Array to trim
* @return array Trimmed array
* @access public
*/
function arrayTrim($array) {
foreach ($array as $key => $value) {
$array[$key] = trim($value);
}
array_unshift($array, "");
return $array;
}
}

View File

@@ -0,0 +1,958 @@
<?php
/**
* Authentication component
*
* Manages user logins and permissions.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', array('Router', 'Security'), false);
/**
* Authentication control component class
*
* Binds access control with user authentication and session management.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1250/Authentication
*/
class AuthComponent extends Object {
/**
* Maintains current user login state.
*
* @var boolean
* @access private
*/
var $_loggedIn = false;
/**
* Other components utilized by AuthComponent
*
* @var array
* @access public
*/
var $components = array('Session', 'RequestHandler');
/**
* A reference to the object used for authentication
*
* @var object
* @access public
* @link http://book.cakephp.org/view/1278/authenticate
*/
var $authenticate = null;
/**
* The name of the component to use for Authorization or set this to
* 'controller' will validate against Controller::isAuthorized()
* 'actions' will validate Controller::action against an AclComponent::check()
* 'crud' will validate mapActions against an AclComponent::check()
* array('model'=> 'name'); will validate mapActions against model $name::isAuthorized(user, controller, mapAction)
* 'object' will validate Controller::action against object::isAuthorized(user, controller, action)
*
* @var mixed
* @access public
* @link http://book.cakephp.org/view/1275/authorize
*/
var $authorize = false;
/**
* The name of an optional view element to render when an Ajax request is made
* with an invalid or expired session
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1277/ajaxLogin
*/
var $ajaxLogin = null;
/**
* The name of the element used for SessionComponent::setFlash
*
* @var string
* @access public
*/
var $flashElement = 'default';
/**
* The name of the model that represents users which will be authenticated. Defaults to 'User'.
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1266/userModel
*/
var $userModel = 'User';
/**
* Additional query conditions to use when looking up and authenticating users,
* i.e. array('User.is_active' => 1).
*
* @var array
* @access public
* @link http://book.cakephp.org/view/1268/userScope
*/
var $userScope = array();
/**
* Allows you to specify non-default login name and password fields used in
* $userModel, i.e. array('username' => 'login_name', 'password' => 'passwd').
*
* @var array
* @access public
* @link http://book.cakephp.org/view/1267/fields
*/
var $fields = array('username' => 'username', 'password' => 'password');
/**
* The session key name where the record of the current user is stored. If
* unspecified, it will be "Auth.{$userModel name}".
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1276/sessionKey
*/
var $sessionKey = null;
/**
* If using action-based access control, this defines how the paths to action
* ACO nodes is computed. If, for example, all controller nodes are nested
* under an ACO node named 'Controllers', $actionPath should be set to
* "Controllers/".
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1279/actionPath
*/
var $actionPath = null;
/**
* A URL (defined as a string or array) to the controller action that handles
* logins.
*
* @var mixed
* @access public
* @link http://book.cakephp.org/view/1269/loginAction
*/
var $loginAction = null;
/**
* Normally, if a user is redirected to the $loginAction page, the location they
* were redirected from will be stored in the session so that they can be
* redirected back after a successful login. If this session value is not
* set, the user will be redirected to the page specified in $loginRedirect.
*
* @var mixed
* @access public
* @link http://book.cakephp.org/view/1270/loginRedirect
*/
var $loginRedirect = null;
/**
* The default action to redirect to after the user is logged out. While AuthComponent does
* not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout().
* Defaults to AuthComponent::$loginAction.
*
* @var mixed
* @access public
* @see AuthComponent::$loginAction
* @see AuthComponent::logout()
* @link http://book.cakephp.org/view/1271/logoutRedirect
*/
var $logoutRedirect = null;
/**
* The name of model or model object, or any other object has an isAuthorized method.
*
* @var string
* @access public
*/
var $object = null;
/**
* Error to display when user login fails. For security purposes, only one error is used for all
* login failures, so as not to expose information on why the login failed.
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1272/loginError
*/
var $loginError = null;
/**
* Error to display when user attempts to access an object or action to which they do not have
* acccess.
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1273/authError
*/
var $authError = null;
/**
* Determines whether AuthComponent will automatically redirect and exit if login is successful.
*
* @var boolean
* @access public
* @link http://book.cakephp.org/view/1274/autoRedirect
*/
var $autoRedirect = true;
/**
* Controller actions for which user validation is not required.
*
* @var array
* @access public
* @see AuthComponent::allow()
* @link http://book.cakephp.org/view/1251/Setting-Auth-Component-Variables
*/
var $allowedActions = array();
/**
* Maps actions to CRUD operations. Used for controller-based validation ($validate = 'controller').
*
* @var array
* @access public
* @see AuthComponent::mapActions()
*/
var $actionMap = array(
'index' => 'read',
'add' => 'create',
'edit' => 'update',
'view' => 'read',
'remove' => 'delete'
);
/**
* Form data from Controller::$data
*
* @var array
* @access public
*/
var $data = array();
/**
* Parameter data from Controller::$params
*
* @var array
* @access public
*/
var $params = array();
/**
* Method list for bound controller
*
* @var array
* @access protected
*/
var $_methods = array();
/**
* Initializes AuthComponent for use in the controller
*
* @param object $controller A reference to the instantiating controller object
* @return void
* @access public
*/
function initialize(&$controller, $settings = array()) {
$this->params = $controller->params;
$crud = array('create', 'read', 'update', 'delete');
$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
$this->_methods = $controller->methods;
$prefixes = Router::prefixes();
if (!empty($prefixes)) {
foreach ($prefixes as $prefix) {
$this->actionMap = array_merge($this->actionMap, array(
$prefix . '_index' => 'read',
$prefix . '_add' => 'create',
$prefix . '_edit' => 'update',
$prefix . '_view' => 'read',
$prefix . '_remove' => 'delete',
$prefix . '_create' => 'create',
$prefix . '_read' => 'read',
$prefix . '_update' => 'update',
$prefix . '_delete' => 'delete'
));
}
}
$this->_set($settings);
if (Configure::read() > 0) {
App::import('Debugger');
Debugger::checkSecurityKeys();
}
}
/**
* Main execution method. Handles redirecting of invalid users, and processing
* of login form data.
*
* @param object $controller A reference to the instantiating controller object
* @return boolean
* @access public
*/
function startup(&$controller) {
$isErrorOrTests = (
strtolower($controller->name) == 'cakeerror' ||
(strtolower($controller->name) == 'tests' && Configure::read() > 0)
);
if ($isErrorOrTests) {
return true;
}
$methods = array_flip($controller->methods);
$action = strtolower($controller->params['action']);
$isMissingAction = (
$controller->scaffold === false &&
!isset($methods[$action])
);
if ($isMissingAction) {
return true;
}
if (!$this->__setDefaults()) {
return false;
}
$this->data = $controller->data = $this->hashPasswords($controller->data);
$url = '';
if (isset($controller->params['url']['url'])) {
$url = $controller->params['url']['url'];
}
$url = Router::normalize($url);
$loginAction = Router::normalize($this->loginAction);
$allowedActions = array_map('strtolower', $this->allowedActions);
$isAllowed = (
$this->allowedActions == array('*') ||
in_array($action, $allowedActions)
);
if ($loginAction != $url && $isAllowed) {
return true;
}
if ($loginAction == $url) {
$model =& $this->getModel();
if (empty($controller->data) || !isset($controller->data[$model->alias])) {
if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
$this->Session->write('Auth.redirect', $controller->referer(null, true));
}
return false;
}
$isValid = !empty($controller->data[$model->alias][$this->fields['username']]) &&
!empty($controller->data[$model->alias][$this->fields['password']]);
if ($isValid) {
$username = $controller->data[$model->alias][$this->fields['username']];
$password = $controller->data[$model->alias][$this->fields['password']];
$data = array(
$model->alias . '.' . $this->fields['username'] => $username,
$model->alias . '.' . $this->fields['password'] => $password
);
if ($this->login($data)) {
if ($this->autoRedirect) {
$controller->redirect($this->redirect(), null, true);
}
return true;
}
}
$this->Session->setFlash($this->loginError, $this->flashElement, array(), 'auth');
$controller->data[$model->alias][$this->fields['password']] = null;
return false;
} else {
if (!$this->user()) {
if (!$this->RequestHandler->isAjax()) {
$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
if (!empty($controller->params['url']) && count($controller->params['url']) >= 2) {
$query = $controller->params['url'];
unset($query['url'], $query['ext']);
$url .= Router::queryString($query, array());
}
$this->Session->write('Auth.redirect', $url);
$controller->redirect($loginAction);
return false;
} elseif (!empty($this->ajaxLogin)) {
$controller->viewPath = 'elements';
echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
$this->_stop();
return false;
} else {
$controller->redirect(null, 403);
}
}
}
if (!$this->authorize) {
return true;
}
extract($this->__authType());
switch ($type) {
case 'controller':
$this->object =& $controller;
break;
case 'crud':
case 'actions':
if (isset($controller->Acl)) {
$this->Acl =& $controller->Acl;
} else {
trigger_error(__('Could not find AclComponent. Please include Acl in Controller::$components.', true), E_USER_WARNING);
}
break;
case 'model':
if (!isset($object)) {
$hasModel = (
isset($controller->{$controller->modelClass}) &&
is_object($controller->{$controller->modelClass})
);
$isUses = (
!empty($controller->uses) && isset($controller->{$controller->uses[0]}) &&
is_object($controller->{$controller->uses[0]})
);
if ($hasModel) {
$object = $controller->modelClass;
} elseif ($isUses) {
$object = $controller->uses[0];
}
}
$type = array('model' => $object);
break;
}
if ($this->isAuthorized($type)) {
return true;
}
$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
$controller->redirect($controller->referer(), null, true);
return false;
}
/**
* Attempts to introspect the correct values for object properties including
* $userModel and $sessionKey.
*
* @param object $controller A reference to the instantiating controller object
* @return boolean
* @access private
*/
function __setDefaults() {
if (empty($this->userModel)) {
trigger_error(__("Could not find \$userModel. Please set AuthComponent::\$userModel in beforeFilter().", true), E_USER_WARNING);
return false;
}
list($plugin, $model) = pluginSplit($this->userModel);
$defaults = array(
'loginAction' => array(
'controller' => Inflector::underscore(Inflector::pluralize($model)),
'action' => 'login',
'plugin' => Inflector::underscore($plugin),
),
'sessionKey' => 'Auth.' . $model,
'logoutRedirect' => $this->loginAction,
'loginError' => __('Login failed. Invalid username or password.', true),
'authError' => __('You are not authorized to access that location.', true)
);
foreach ($defaults as $key => $value) {
if (empty($this->{$key})) {
$this->{$key} = $value;
}
}
return true;
}
/**
* Determines whether the given user is authorized to perform an action. The type of
* authorization used is based on the value of AuthComponent::$authorize or the
* passed $type param.
*
* Types:
* 'controller' will validate against Controller::isAuthorized() if controller instance is
* passed in $object
* 'actions' will validate Controller::action against an AclComponent::check()
* 'crud' will validate mapActions against an AclComponent::check()
* array('model'=> 'name'); will validate mapActions against model
* $name::isAuthorized(user, controller, mapAction)
* 'object' will validate Controller::action against
* object::isAuthorized(user, controller, action)
*
* @param string $type Type of authorization
* @param mixed $object object, model object, or model name
* @param mixed $user The user to check the authorization of
* @return boolean True if $user is authorized, otherwise false
* @access public
*/
function isAuthorized($type = null, $object = null, $user = null) {
if (empty($user) && !$this->user()) {
return false;
} elseif (empty($user)) {
$user = $this->user();
}
extract($this->__authType($type));
if (!$object) {
$object = $this->object;
}
$valid = false;
switch ($type) {
case 'controller':
$valid = $object->isAuthorized();
break;
case 'actions':
$valid = $this->Acl->check($user, $this->action());
break;
case 'crud':
if (!isset($this->actionMap[$this->params['action']])) {
trigger_error(
sprintf(__('Auth::startup() - Attempted access of un-mapped action "%1$s" in controller "%2$s"', true), $this->params['action'], $this->params['controller']),
E_USER_WARNING
);
} else {
$valid = $this->Acl->check(
$user,
$this->action(':controller'),
$this->actionMap[$this->params['action']]
);
}
break;
case 'model':
$action = $this->params['action'];
if (isset($this->actionMap[$action])) {
$action = $this->actionMap[$action];
}
if (is_string($object)) {
$object = $this->getModel($object);
}
case 'object':
if (!isset($action)) {
$action = $this->action(':action');
}
if (empty($object)) {
trigger_error(sprintf(__('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', true), get_class($object)), E_USER_WARNING);
return;
}
if (method_exists($object, 'isAuthorized')) {
$valid = $object->isAuthorized($user, $this->action(':controller'), $action);
} elseif ($object) {
trigger_error(sprintf(__('%s::isAuthorized() is not defined.', true), get_class($object)), E_USER_WARNING);
}
break;
case null:
case false:
return true;
break;
default:
trigger_error(__('Auth::isAuthorized() - $authorize is set to an incorrect value. Allowed settings are: "actions", "crud", "model" or null.', true), E_USER_WARNING);
break;
}
return $valid;
}
/**
* Get authorization type
*
* @param string $auth Type of authorization
* @return array Associative array with: type, object
* @access private
*/
function __authType($auth = null) {
if ($auth == null) {
$auth = $this->authorize;
}
$object = null;
if (is_array($auth)) {
$type = key($auth);
$object = $auth[$type];
} else {
$type = $auth;
return compact('type');
}
return compact('type', 'object');
}
/**
* Takes a list of actions in the current controller for which authentication is not required, or
* no parameters to allow all actions.
*
* @param mixed $action Controller action name or array of actions
* @param string $action Controller action name
* @param string ... etc.
* @return void
* @access public
* @link http://book.cakephp.org/view/1257/allow
*/
function allow() {
$args = func_get_args();
if (empty($args) || $args == array('*')) {
$this->allowedActions = $this->_methods;
} else {
if (isset($args[0]) && is_array($args[0])) {
$args = $args[0];
}
$this->allowedActions = array_merge($this->allowedActions, array_map('strtolower', $args));
}
}
/**
* Removes items from the list of allowed actions.
*
* @param mixed $action Controller action name or array of actions
* @param string $action Controller action name
* @param string ... etc.
* @return void
* @see AuthComponent::allow()
* @access public
* @link http://book.cakephp.org/view/1258/deny
*/
function deny() {
$args = func_get_args();
if (isset($args[0]) && is_array($args[0])) {
$args = $args[0];
}
foreach ($args as $arg) {
$i = array_search(strtolower($arg), $this->allowedActions);
if (is_int($i)) {
unset($this->allowedActions[$i]);
}
}
$this->allowedActions = array_values($this->allowedActions);
}
/**
* Maps action names to CRUD operations. Used for controller-based authentication.
*
* @param array $map Actions to map
* @return void
* @access public
* @link http://book.cakephp.org/view/1260/mapActions
*/
function mapActions($map = array()) {
$crud = array('create', 'read', 'update', 'delete');
foreach ($map as $action => $type) {
if (in_array($action, $crud) && is_array($type)) {
foreach ($type as $typedAction) {
$this->actionMap[$typedAction] = $action;
}
} else {
$this->actionMap[$action] = $type;
}
}
}
/**
* Manually log-in a user with the given parameter data. The $data provided can be any data
* structure used to identify a user in AuthComponent::identify(). If $data is empty or not
* specified, POST data from Controller::$data will be used automatically.
*
* After (if) login is successful, the user record is written to the session key specified in
* AuthComponent::$sessionKey.
*
* @param mixed $data User object
* @return boolean True on login success, false on failure
* @access public
* @link http://book.cakephp.org/view/1261/login
*/
function login($data = null) {
$this->__setDefaults();
$this->_loggedIn = false;
if (empty($data)) {
$data = $this->data;
}
if ($user = $this->identify($data)) {
$this->Session->write($this->sessionKey, $user);
$this->_loggedIn = true;
}
return $this->_loggedIn;
}
/**
* Logs a user out, and returns the login action to redirect to.
*
* @param mixed $url Optional URL to redirect the user to after logout
* @return string AuthComponent::$loginAction
* @see AuthComponent::$loginAction
* @access public
* @link http://book.cakephp.org/view/1262/logout
*/
function logout() {
$this->__setDefaults();
$this->Session->delete($this->sessionKey);
$this->Session->delete('Auth.redirect');
$this->_loggedIn = false;
return Router::normalize($this->logoutRedirect);
}
/**
* Get the current user from the session.
*
* @param string $key field to retrive. Leave null to get entire User record
* @return mixed User record. or null if no user is logged in.
* @access public
* @link http://book.cakephp.org/view/1264/user
*/
function user($key = null) {
$this->__setDefaults();
if (!$this->Session->check($this->sessionKey)) {
return null;
}
if ($key == null) {
$model =& $this->getModel();
return array($model->alias => $this->Session->read($this->sessionKey));
} else {
$user = $this->Session->read($this->sessionKey);
if (isset($user[$key])) {
return $user[$key];
}
return null;
}
}
/**
* If no parameter is passed, gets the authentication redirect URL.
*
* @param mixed $url Optional URL to write as the login redirect URL.
* @return string Redirect URL
* @access public
*/
function redirect($url = null) {
if (!is_null($url)) {
$redir = $url;
$this->Session->write('Auth.redirect', $redir);
} elseif ($this->Session->check('Auth.redirect')) {
$redir = $this->Session->read('Auth.redirect');
$this->Session->delete('Auth.redirect');
if (Router::normalize($redir) == Router::normalize($this->loginAction)) {
$redir = $this->loginRedirect;
}
} else {
$redir = $this->loginRedirect;
}
return Router::normalize($redir);
}
/**
* Validates a user against an abstract object.
*
* @param mixed $object The object to validate the user against.
* @param mixed $user Optional. The identity of the user to be validated.
* Uses the current user session if none specified. For
* valid forms of identifying users, see
* AuthComponent::identify().
* @param string $action Optional. The action to validate against.
* @see AuthComponent::identify()
* @return boolean True if the user validates, false otherwise.
* @access public
*/
function validate($object, $user = null, $action = null) {
if (empty($user)) {
$user = $this->user();
}
if (empty($user)) {
return false;
}
return $this->Acl->check($user, $object, $action);
}
/**
* Returns the path to the ACO node bound to a controller/action.
*
* @param string $action Optional. The controller/action path to validate the
* user against. The current request action is used if
* none is specified.
* @return boolean ACO node path
* @access public
* @link http://book.cakephp.org/view/1256/action
*/
function action($action = ':plugin/:controller/:action') {
$plugin = empty($this->params['plugin']) ? null : Inflector::camelize($this->params['plugin']) . '/';
return str_replace(
array(':controller', ':action', ':plugin/'),
array(Inflector::camelize($this->params['controller']), $this->params['action'], $plugin),
$this->actionPath . $action
);
}
/**
* Returns a reference to the model object specified, and attempts
* to load it if it is not found.
*
* @param string $name Model name (defaults to AuthComponent::$userModel)
* @return object A reference to a model object
* @access public
*/
function &getModel($name = null) {
$model = null;
if (!$name) {
$name = $this->userModel;
}
if (PHP5) {
$model = ClassRegistry::init($name);
} else {
$model =& ClassRegistry::init($name);
}
if (empty($model)) {
trigger_error(__('Auth::getModel() - Model is not set or could not be found', true), E_USER_WARNING);
return null;
}
return $model;
}
/**
* Identifies a user based on specific criteria.
*
* @param mixed $user Optional. The identity of the user to be validated.
* Uses the current user session if none specified.
* @param array $conditions Optional. Additional conditions to a find.
* @return array User record data, or null, if the user could not be identified.
* @access public
*/
function identify($user = null, $conditions = null) {
if ($conditions === false) {
$conditions = null;
} elseif (is_array($conditions)) {
$conditions = array_merge((array)$this->userScope, $conditions);
} else {
$conditions = $this->userScope;
}
$model =& $this->getModel();
if (empty($user)) {
$user = $this->user();
if (empty($user)) {
return null;
}
} elseif (is_object($user) && is_a($user, 'Model')) {
if (!$user->exists()) {
return null;
}
$user = $user->read();
$user = $user[$model->alias];
} elseif (is_array($user) && isset($user[$model->alias])) {
$user = $user[$model->alias];
}
if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$model->alias . '.' . $this->fields['username']]))) {
if (isset($user[$this->fields['username']]) && !empty($user[$this->fields['username']]) && !empty($user[$this->fields['password']])) {
if (trim($user[$this->fields['username']]) == '=' || trim($user[$this->fields['password']]) == '=') {
return false;
}
$find = array(
$model->alias.'.'.$this->fields['username'] => $user[$this->fields['username']],
$model->alias.'.'.$this->fields['password'] => $user[$this->fields['password']]
);
} elseif (isset($user[$model->alias . '.' . $this->fields['username']]) && !empty($user[$model->alias . '.' . $this->fields['username']])) {
if (trim($user[$model->alias . '.' . $this->fields['username']]) == '=' || trim($user[$model->alias . '.' . $this->fields['password']]) == '=') {
return false;
}
$find = array(
$model->alias.'.'.$this->fields['username'] => $user[$model->alias . '.' . $this->fields['username']],
$model->alias.'.'.$this->fields['password'] => $user[$model->alias . '.' . $this->fields['password']]
);
} else {
return false;
}
$data = $model->find('first', array(
'conditions' => array_merge($find, $conditions),
'recursive' => 0
));
if (empty($data) || empty($data[$model->alias])) {
return null;
}
} elseif (!empty($user) && is_string($user)) {
$data = $model->find('first', array(
'conditions' => array_merge(array($model->escapeField() => $user), $conditions),
));
if (empty($data) || empty($data[$model->alias])) {
return null;
}
}
if (!empty($data)) {
if (!empty($data[$model->alias][$this->fields['password']])) {
unset($data[$model->alias][$this->fields['password']]);
}
return $data[$model->alias];
}
return null;
}
/**
* Hash any passwords found in $data using $userModel and $fields['password']
*
* @param array $data Set of data to look for passwords
* @return array Data with passwords hashed
* @access public
* @link http://book.cakephp.org/view/1259/hashPasswords
*/
function hashPasswords($data) {
if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
return $this->authenticate->hashPasswords($data);
}
if (is_array($data)) {
$model =& $this->getModel();
if(isset($data[$model->alias])) {
if (isset($data[$model->alias][$this->fields['username']]) && isset($data[$model->alias][$this->fields['password']])) {
$data[$model->alias][$this->fields['password']] = $this->password($data[$model->alias][$this->fields['password']]);
}
}
}
return $data;
}
/**
* Hash a password with the application's salt value (as defined with Configure::write('Security.salt');
*
* @param string $password Password to hash
* @return string Hashed password
* @access public
* @link http://book.cakephp.org/view/1263/password
*/
function password($password) {
return Security::hash($password, null, true);
}
/**
* Component shutdown. If user is logged in, wipe out redirect.
*
* @param object $controller Instantiating controller
* @access public
*/
function shutdown(&$controller) {
if ($this->_loggedIn) {
$this->Session->delete('Auth.redirect');
}
}
}

View File

@@ -0,0 +1,471 @@
<?php
/**
* Cookie Component
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 1.2.0.4213
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Load Security class
*/
App::import('Core', 'Security');
/**
* Cookie Component.
*
* Cookie handling for the controller.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1280/Cookies
*
*/
class CookieComponent extends Object {
/**
* The name of the cookie.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->name = 'CookieName';
*
* @var string
* @access public
*/
var $name = 'CakeCookie';
/**
* The time a cookie will remain valid.
*
* Can be either integer Unix timestamp or a date string.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->time = '5 Days';
*
* @var mixed
* @access public
*/
var $time = null;
/**
* Cookie path.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->path = '/';
*
* The path on the server in which the cookie will be available on.
* If var $cookiePath is set to '/foo/', the cookie will only be available
* within the /foo/ directory and all sub-directories such as /foo/bar/ of domain.
* The default value is the entire domain.
*
* @var string
* @access public
*/
var $path = '/';
/**
* Domain path.
*
* The domain that the cookie is available.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->domain = '.example.com';
*
* To make the cookie available on all subdomains of example.com.
* Set $this->Cookie->domain = '.example.com'; in your controller beforeFilter
*
* @var string
* @access public
*/
var $domain = '';
/**
* Secure HTTPS only cookie.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->secure = true;
*
* Indicates that the cookie should only be transmitted over a secure HTTPS connection.
* When set to true, the cookie will only be set if a secure connection exists.
*
* @var boolean
* @access public
*/
var $secure = false;
/**
* Encryption key.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->key = 'SomeRandomString';
*
* @var string
* @access protected
*/
var $key = null;
/**
* Values stored in the cookie.
*
* Accessed in the controller using $this->Cookie->read('Name.key');
*
* @see CookieComponent::read();
* @var string
* @access private
*/
var $__values = array();
/**
* Type of encryption to use.
*
* Currently only one method is available
* Defaults to Security::cipher();
*
* @var string
* @access private
* @todo add additional encryption methods
*/
var $__type = 'cipher';
/**
* Used to reset cookie time if $expire is passed to CookieComponent::write()
*
* @var string
* @access private
*/
var $__reset = null;
/**
* Expire time of the cookie
*
* This is controlled by CookieComponent::time;
*
* @var string
* @access private
*/
var $__expires = 0;
/**
* Main execution method.
*
* @param object $controller A reference to the instantiating controller object
* @access public
*/
function initialize(&$controller, $settings) {
$this->key = Configure::read('Security.salt');
$this->_set($settings);
}
/**
* Start CookieComponent for use in the controller
*
* @access public
*/
function startup() {
$this->__expire($this->time);
if (isset($_COOKIE[$this->name])) {
$this->__values = $this->__decrypt($_COOKIE[$this->name]);
}
}
/**
* Write a value to the $_COOKIE[$key];
*
* Optional [Name.], reguired key, optional $value, optional $encrypt, optional $expires
* $this->Cookie->write('[Name.]key, $value);
*
* By default all values are encrypted.
* You must pass $encrypt false to store values in clear test
*
* You must use this method before any output is sent to the browser.
* Failure to do so will result in header already sent errors.
*
* @param mixed $key Key for the value
* @param mixed $value Value
* @param boolean $encrypt Set to true to encrypt value, false otherwise
* @param string $expires Can be either Unix timestamp, or date string
* @access public
*/
function write($key, $value = null, $encrypt = true, $expires = null) {
if (is_null($encrypt)) {
$encrypt = true;
}
$this->__encrypted = $encrypt;
$this->__expire($expires);
if (!is_array($key)) {
$key = array($key => $value);
}
foreach ($key as $name => $value) {
if (strpos($name, '.') === false) {
$this->__values[$name] = $value;
$this->__write("[$name]", $value);
} else {
$names = explode('.', $name, 2);
if (!isset($this->__values[$names[0]])) {
$this->__values[$names[0]] = array();
}
$this->__values[$names[0]] = Set::insert($this->__values[$names[0]], $names[1], $value);
$this->__write('[' . implode('][', $names) . ']', $value);
}
}
$this->__encrypted = true;
}
/**
* Read the value of the $_COOKIE[$key];
*
* Optional [Name.], reguired key
* $this->Cookie->read(Name.key);
*
* @param mixed $key Key of the value to be obtained. If none specified, obtain map key => values
* @return string or null, value for specified key
* @access public
*/
function read($key = null) {
if (empty($this->__values) && isset($_COOKIE[$this->name])) {
$this->__values = $this->__decrypt($_COOKIE[$this->name]);
}
if (is_null($key)) {
return $this->__values;
}
if (strpos($key, '.') !== false) {
$names = explode('.', $key, 2);
$key = $names[0];
}
if (!isset($this->__values[$key])) {
return null;
}
if (!empty($names[1])) {
return Set::extract($this->__values[$key], $names[1]);
}
return $this->__values[$key];
}
/**
* Delete a cookie value
*
* Optional [Name.], reguired key
* $this->Cookie->read('Name.key);
*
* You must use this method before any output is sent to the browser.
* Failure to do so will result in header already sent errors.
*
* @param string $key Key of the value to be deleted
* @return void
* @access public
*/
function delete($key) {
if (empty($this->__values)) {
$this->read();
}
if (strpos($key, '.') === false) {
unset($this->__values[$key]);
$this->__delete("[$key]");
return;
}
$names = explode('.', $key, 2);
$this->__values[$names[0]] = Set::remove($this->__values[$names[0]], $names[1]);
$this->__delete('[' . implode('][', $names) . ']');
}
/**
* Destroy current cookie
*
* You must use this method before any output is sent to the browser.
* Failure to do so will result in header already sent errors.
*
* @return void
* @access public
*/
function destroy() {
if (isset($_COOKIE[$this->name])) {
$this->__values = $this->__decrypt($_COOKIE[$this->name]);
}
foreach ($this->__values as $name => $value) {
if (is_array($value)) {
foreach ($value as $key => $val) {
unset($this->__values[$name][$key]);
$this->__delete("[$name][$key]");
}
}
unset($this->__values[$name]);
$this->__delete("[$name]");
}
}
/**
* Will allow overriding default encryption method.
*
* @param string $type Encryption method
* @access public
* @todo NOT IMPLEMENTED
*/
function type($type = 'cipher') {
$this->__type = 'cipher';
}
/**
* Set the expire time for a session variable.
*
* Creates a new expire time for a session variable.
* $expire can be either integer Unix timestamp or a date string.
*
* Used by write()
* CookieComponent::write(string, string, boolean, 8400);
* CookieComponent::write(string, string, boolean, '5 Days');
*
* @param mixed $expires Can be either Unix timestamp, or date string
* @return int Unix timestamp
* @access private
*/
function __expire($expires = null) {
$now = time();
if (is_null($expires)) {
return $this->__expires;
}
$this->__reset = $this->__expires;
if ($expires == 0) {
return $this->__expires = 0;
}
if (is_integer($expires) || is_numeric($expires)) {
return $this->__expires = $now + intval($expires);
}
return $this->__expires = strtotime($expires, $now);
}
/**
* Set cookie
*
* @param string $name Name for cookie
* @param string $value Value for cookie
* @access private
*/
function __write($name, $value) {
setcookie($this->name . $name, $this->__encrypt($value), $this->__expires, $this->path, $this->domain, $this->secure);
if (!is_null($this->__reset)) {
$this->__expires = $this->__reset;
$this->__reset = null;
}
}
/**
* Sets a cookie expire time to remove cookie value
*
* @param string $name Name of cookie
* @access private
*/
function __delete($name) {
setcookie($this->name . $name, '', time() - 42000, $this->path, $this->domain, $this->secure);
}
/**
* Encrypts $value using var $type method in Security class
*
* @param string $value Value to encrypt
* @return string encrypted string
* @access private
*/
function __encrypt($value) {
if (is_array($value)) {
$value = $this->__implode($value);
}
if ($this->__encrypted === true) {
$type = $this->__type;
$value = "Q2FrZQ==." .base64_encode(Security::$type($value, $this->key));
}
return $value;
}
/**
* Decrypts $value using var $type method in Security class
*
* @param array $values Values to decrypt
* @return string decrypted string
* @access private
*/
function __decrypt($values) {
$decrypted = array();
$type = $this->__type;
foreach ($values as $name => $value) {
if (is_array($value)) {
foreach ($value as $key => $val) {
$pos = strpos($val, 'Q2FrZQ==.');
$decrypted[$name][$key] = $this->__explode($val);
if ($pos !== false) {
$val = substr($val, 8);
$decrypted[$name][$key] = $this->__explode(Security::$type(base64_decode($val), $this->key));
}
}
} else {
$pos = strpos($value, 'Q2FrZQ==.');
$decrypted[$name] = $this->__explode($value);
if ($pos !== false) {
$value = substr($value, 8);
$decrypted[$name] = $this->__explode(Security::$type(base64_decode($value), $this->key));
}
}
}
return $decrypted;
}
/**
* Implode method to keep keys are multidimensional arrays
*
* @param array $array Map of key and values
* @return string String in the form key1|value1,key2|value2
* @access private
*/
function __implode($array) {
$string = '';
foreach ($array as $key => $value) {
$string .= ',' . $key . '|' . $value;
}
return substr($string, 1);
}
/**
* Explode method to return array from string set in CookieComponent::__implode()
*
* @param string $string String in the form key1|value1,key2|value2
* @return array Map of key and values
* @access private
*/
function __explode($string) {
$array = array();
foreach (explode(',', $string) as $pair) {
$key = explode('|', $pair);
if (!isset($key[1])) {
return $key[0];
}
$array[$key[0]] = $key[1];
}
return $array;
}
}

View File

@@ -0,0 +1,962 @@
<?php
/**
* Email Component
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 1.2.0.3467
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', 'Multibyte');
/**
* EmailComponent
*
* This component is used for handling Internet Message Format based
* based on the standard outlined in http://www.rfc-editor.org/rfc/rfc2822.txt
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1283/Email
*
*/
class EmailComponent extends Object{
/**
* Recipient of the email
*
* @var string
* @access public
*/
var $to = null;
/**
* The mail which the email is sent from
*
* @var string
* @access public
*/
var $from = null;
/**
* The email the recipient will reply to
*
* @var string
* @access public
*/
var $replyTo = null;
/**
* The read receipt email
*
* @var string
* @access public
*/
var $readReceipt = null;
/**
* The mail that will be used in case of any errors like
* - Remote mailserver down
* - Remote user has exceeded his quota
* - Unknown user
*
* @var string
* @access public
*/
var $return = null;
/**
* Carbon Copy
*
* List of email's that should receive a copy of the email.
* The Recipient WILL be able to see this list
*
* @var array
* @access public
*/
var $cc = array();
/**
* Blind Carbon Copy
*
* List of email's that should receive a copy of the email.
* The Recipient WILL NOT be able to see this list
*
* @var array
* @access public
*/
var $bcc = array();
/**
* The subject of the email
*
* @var string
* @access public
*/
var $subject = null;
/**
* Associative array of a user defined headers
* Keys will be prefixed 'X-' as per RFC2822 Section 4.7.5
*
* @var array
* @access public
*/
var $headers = array();
/**
* List of additional headers
*
* These will NOT be used if you are using safemode and mail()
*
* @var string
* @access public
*/
var $additionalParams = null;
/**
* Layout for the View
*
* @var string
* @access public
*/
var $layout = 'default';
/**
* Template for the view
*
* @var string
* @access public
*/
var $template = null;
/**
* as per RFC2822 Section 2.1.1
*
* @var integer
* @access public
*/
var $lineLength = 70;
/**
* @deprecated see lineLength
*/
var $_lineLength = null;
/**
* What format should the email be sent in
*
* Supported formats:
* - text
* - html
* - both
*
* @var string
* @access public
*/
var $sendAs = 'text';
/**
* What method should the email be sent by
*
* Supported methods:
* - mail
* - smtp
* - debug
*
* @var string
* @access public
*/
var $delivery = 'mail';
/**
* charset the email is sent in
*
* @var string
* @access public
*/
var $charset = 'utf-8';
/**
* List of files that should be attached to the email.
*
* Can be both absolute and relative paths
*
* @var array
* @access public
*/
var $attachments = array();
/**
* What mailer should EmailComponent identify itself as
*
* @var string
* @access public
*/
var $xMailer = 'CakePHP Email Component';
/**
* The list of paths to search if an attachment isnt absolute
*
* @var array
* @access public
*/
var $filePaths = array();
/**
* List of options to use for smtp mail method
*
* Options is:
* - port
* - host
* - timeout
* - username
* - password
* - client
*
* @var array
* @access public
* @link http://book.cakephp.org/view/1290/Sending-A-Message-Using-SMTP
*/
var $smtpOptions = array();
/**
* Placeholder for any errors that might happen with the
* smtp mail methods
*
* @var string
* @access public
*/
var $smtpError = null;
/**
* Contains the rendered plain text message if one was sent.
*
* @var string
* @access public
*/
var $textMessage = null;
/**
* Contains the rendered HTML message if one was sent.
*
* @var string
* @access public
*/
var $htmlMessage = null;
/**
* Whether to generate a Message-ID header for the
* e-mail. True to generate a Message-ID, False to let
* it be handled by sendmail (or similar) or a string
* to completely override the Message-ID.
*
* @var mixed
* @access public
*/
var $messageId = true;
/**
* Temporary store of message header lines
*
* @var array
* @access private
*/
var $__header = array();
/**
* If set, boundary to use for multipart mime messages
*
* @var string
* @access private
*/
var $__boundary = null;
/**
* Temporary store of message lines
*
* @var array
* @access private
*/
var $__message = array();
/**
* Variable that holds SMTP connection
*
* @var resource
* @access private
*/
var $__smtpConnection = null;
/**
* Initialize component
*
* @param object $controller Instantiating controller
* @access public
*/
function initialize(&$controller, $settings = array()) {
$this->Controller =& $controller;
if (Configure::read('App.encoding') !== null) {
$this->charset = Configure::read('App.encoding');
}
$this->_set($settings);
}
/**
* Startup component
*
* @param object $controller Instantiating controller
* @access public
*/
function startup(&$controller) {}
/**
* Send an email using the specified content, template and layout
*
* @param mixed $content Either an array of text lines, or a string with contents
* If you are rendering a template this variable will be sent to the templates as `$content`
* @param string $template Template to use when sending email
* @param string $layout Layout to use to enclose email body
* @return boolean Success
* @access public
*/
function send($content = null, $template = null, $layout = null) {
$this->_createHeader();
if ($template) {
$this->template = $template;
}
if ($layout) {
$this->layout = $layout;
}
if (is_array($content)) {
$content = implode("\n", $content) . "\n";
}
$this->htmlMessage = $this->textMessage = null;
if ($content) {
if ($this->sendAs === 'html') {
$this->htmlMessage = $content;
} elseif ($this->sendAs === 'text') {
$this->textMessage = $content;
} else {
$this->htmlMessage = $this->textMessage = $content;
}
}
if ($this->sendAs === 'text') {
$message = $this->_wrap($content);
} else {
$message = $this->_wrap($content, 998);
}
if ($this->template === null) {
$message = $this->_formatMessage($message);
} else {
$message = $this->_render($message);
}
$message[] = '';
$this->__message = $message;
if (!empty($this->attachments)) {
$this->_attachFiles();
}
if (!is_null($this->__boundary)) {
$this->__message[] = '';
$this->__message[] = '--' . $this->__boundary . '--';
$this->__message[] = '';
}
$_method = '_' . $this->delivery;
$sent = $this->$_method();
$this->__header = array();
$this->__message = array();
return $sent;
}
/**
* Reset all EmailComponent internal variables to be able to send out a new email.
*
* @access public
* @link http://book.cakephp.org/view/1285/Sending-Multiple-Emails-in-a-loop
*/
function reset() {
$this->template = null;
$this->to = array();
$this->from = null;
$this->replyTo = null;
$this->return = null;
$this->cc = array();
$this->bcc = array();
$this->subject = null;
$this->additionalParams = null;
$this->smtpError = null;
$this->attachments = array();
$this->htmlMessage = null;
$this->textMessage = null;
$this->messageId = true;
$this->__header = array();
$this->__boundary = null;
$this->__message = array();
}
/**
* Render the contents using the current layout and template.
*
* @param string $content Content to render
* @return array Email ready to be sent
* @access private
*/
function _render($content) {
$viewClass = $this->Controller->view;
if ($viewClass != 'View') {
list($plugin, $viewClass) = pluginSplit($viewClass);
$viewClass = $viewClass . 'View';
App::import('View', $this->Controller->view);
}
$View = new $viewClass($this->Controller, false);
$View->layout = $this->layout;
$msg = array();
$content = implode("\n", $content);
if ($this->sendAs === 'both') {
$htmlContent = $content;
if (!empty($this->attachments)) {
$msg[] = '--' . $this->__boundary;
$msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"';
$msg[] = '';
}
$msg[] = '--alt-' . $this->__boundary;
$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
$content = $View->element('email' . DS . 'text' . DS . $this->template, array('content' => $content), true);
$View->layoutPath = 'email' . DS . 'text';
$content = explode("\n", $this->textMessage = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
$msg = array_merge($msg, $content);
$msg[] = '';
$msg[] = '--alt-' . $this->__boundary;
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
$htmlContent = $View->element('email' . DS . 'html' . DS . $this->template, array('content' => $htmlContent), true);
$View->layoutPath = 'email' . DS . 'html';
$htmlContent = explode("\n", $this->htmlMessage = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($htmlContent)));
$msg = array_merge($msg, $htmlContent);
$msg[] = '';
$msg[] = '--alt-' . $this->__boundary . '--';
$msg[] = '';
return $msg;
}
if (!empty($this->attachments)) {
if ($this->sendAs === 'html') {
$msg[] = '';
$msg[] = '--' . $this->__boundary;
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
} else {
$msg[] = '--' . $this->__boundary;
$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
}
}
$content = $View->element('email' . DS . $this->sendAs . DS . $this->template, array('content' => $content), true);
$View->layoutPath = 'email' . DS . $this->sendAs;
$content = explode("\n", $rendered = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
if ($this->sendAs === 'html') {
$this->htmlMessage = $rendered;
} else {
$this->textMessage = $rendered;
}
$msg = array_merge($msg, $content);
return $msg;
}
/**
* Create unique boundary identifier
*
* @access private
*/
function _createboundary() {
$this->__boundary = md5(uniqid(time()));
}
/**
* Sets headers for the message
*
* @access public
* @param array Associative array containing headers to be set.
*/
function header($headers) {
foreach ($headers as $header => $value) {
$this->__header[] = sprintf('%s: %s', trim($header), trim($value));
}
}
/**
* Create emails headers including (but not limited to) from email address, reply to,
* bcc and cc.
*
* @access private
*/
function _createHeader() {
$headers = array();
if ($this->delivery == 'smtp') {
if (is_array($this->to)) {
$headers['To'] = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
} else {
$headers['To'] = $this->_formatAddress($this->to);
}
}
$headers['From'] = $this->_formatAddress($this->from);
if (!empty($this->replyTo)) {
$headers['Reply-To'] = $this->_formatAddress($this->replyTo);
}
if (!empty($this->return)) {
$headers['Return-Path'] = $this->_formatAddress($this->return);
}
if (!empty($this->readReceipt)) {
$headers['Disposition-Notification-To'] = $this->_formatAddress($this->readReceipt);
}
if (!empty($this->cc)) {
$headers['cc'] = implode(', ', array_map(array($this, '_formatAddress'), $this->cc));
}
if (!empty($this->bcc) && $this->delivery != 'smtp') {
$headers['Bcc'] = implode(', ', array_map(array($this, '_formatAddress'), $this->bcc));
}
if ($this->delivery == 'smtp') {
$headers['Subject'] = $this->_encode($this->subject);
}
if ($this->messageId !== false) {
if ($this->messageId === true) {
$headers['Message-ID'] = '<' . String::UUID() . '@' . env('HTTP_HOST') . '>';
} else {
$headers['Message-ID'] = $this->messageId;
}
}
$headers['X-Mailer'] = $this->xMailer;
if (!empty($this->headers)) {
foreach ($this->headers as $key => $val) {
$headers['X-' . $key] = $val;
}
}
if (!empty($this->attachments)) {
$this->_createBoundary();
$headers['MIME-Version'] = '1.0';
$headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->__boundary . '"';
$headers[] = 'This part of the E-mail should never be seen. If';
$headers[] = 'you are reading this, consider upgrading your e-mail';
$headers[] = 'client to a MIME-compatible client.';
} elseif ($this->sendAs === 'text') {
$headers['Content-Type'] = 'text/plain; charset=' . $this->charset;
} elseif ($this->sendAs === 'html') {
$headers['Content-Type'] = 'text/html; charset=' . $this->charset;
} elseif ($this->sendAs === 'both') {
$headers['Content-Type'] = 'multipart/alternative; boundary="alt-' . $this->__boundary . '"';
}
$headers['Content-Transfer-Encoding'] = '7bit';
$this->header($headers);
}
/**
* Format the message by seeing if it has attachments.
*
* @param string $message Message to format
* @access private
*/
function _formatMessage($message) {
if (!empty($this->attachments)) {
$prefix = array('--' . $this->__boundary);
if ($this->sendAs === 'text') {
$prefix[] = 'Content-Type: text/plain; charset=' . $this->charset;
} elseif ($this->sendAs === 'html') {
$prefix[] = 'Content-Type: text/html; charset=' . $this->charset;
} elseif ($this->sendAs === 'both') {
$prefix[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"';
}
$prefix[] = 'Content-Transfer-Encoding: 7bit';
$prefix[] = '';
$message = array_merge($prefix, $message);
}
return $message;
}
/**
* Attach files by adding file contents inside boundaries.
*
* @access private
* @TODO: modify to use the core File class?
*/
function _attachFiles() {
$files = array();
foreach ($this->attachments as $filename => $attachment) {
$file = $this->_findFiles($attachment);
if (!empty($file)) {
if (is_int($filename)) {
$filename = basename($file);
}
$files[$filename] = $file;
}
}
foreach ($files as $filename => $file) {
$handle = fopen($file, 'rb');
$data = fread($handle, filesize($file));
$data = chunk_split(base64_encode($data)) ;
fclose($handle);
$this->__message[] = '--' . $this->__boundary;
$this->__message[] = 'Content-Type: application/octet-stream';
$this->__message[] = 'Content-Transfer-Encoding: base64';
$this->__message[] = 'Content-Disposition: attachment; filename="' . basename($filename) . '"';
$this->__message[] = '';
$this->__message[] = $data;
$this->__message[] = '';
}
}
/**
* Find the specified attachment in the list of file paths
*
* @param string $attachment Attachment file name to find
* @return string Path to located file
* @access private
*/
function _findFiles($attachment) {
if (file_exists($attachment)) {
return $attachment;
}
foreach ($this->filePaths as $path) {
if (file_exists($path . DS . $attachment)) {
$file = $path . DS . $attachment;
return $file;
}
}
return null;
}
/**
* Wrap the message using EmailComponent::$lineLength
*
* @param string $message Message to wrap
* @param integer $lineLength Max length of line
* @return array Wrapped message
* @access protected
*/
function _wrap($message, $lineLength = null) {
$message = $this->_strip($message, true);
$message = str_replace(array("\r\n","\r"), "\n", $message);
$lines = explode("\n", $message);
$formatted = array();
if ($this->_lineLength !== null) {
trigger_error(__('_lineLength cannot be accessed please use lineLength', true), E_USER_WARNING);
$this->lineLength = $this->_lineLength;
}
if (!$lineLength) {
$lineLength = $this->lineLength;
}
foreach ($lines as $line) {
if (substr($line, 0, 1) == '.') {
$line = '.' . $line;
}
$formatted = array_merge($formatted, explode("\n", wordwrap($line, $lineLength, "\n", true)));
}
$formatted[] = '';
return $formatted;
}
/**
* Encode the specified string using the current charset
*
* @param string $subject String to encode
* @return string Encoded string
* @access private
*/
function _encode($subject) {
$subject = $this->_strip($subject);
$nl = "\r\n";
if ($this->delivery == 'mail') {
$nl = '';
}
$internalEncoding = function_exists('mb_internal_encoding');
if ($internalEncoding) {
$restore = mb_internal_encoding();
mb_internal_encoding($this->charset);
}
$return = mb_encode_mimeheader($subject, $this->charset, 'B', $nl);
if ($internalEncoding) {
mb_internal_encoding($restore);
}
return $return;
}
/**
* Format a string as an email address
*
* @param string $string String representing an email address
* @return string Email address suitable for email headers or smtp pipe
* @access private
*/
function _formatAddress($string, $smtp = false) {
$hasAlias = preg_match('/((.*)\s)?<(.+)>/', $string, $matches);
if ($smtp && $hasAlias) {
return $this->_strip('<' . $matches[3] . '>');
} elseif ($smtp) {
return $this->_strip('<' . $string . '>');
}
if ($hasAlias && !empty($matches[2])) {
return $this->_strip($matches[2] . ' <' . $matches[3] . '>');
}
return $this->_strip($string);
}
/**
* Remove certain elements (such as bcc:, to:, %0a) from given value.
* Helps prevent header injection / mainipulation on user content.
*
* @param string $value Value to strip
* @param boolean $message Set to true to indicate main message content
* @return string Stripped value
* @access private
*/
function _strip($value, $message = false) {
$search = '%0a|%0d|Content-(?:Type|Transfer-Encoding)\:';
$search .= '|charset\=|mime-version\:|multipart/mixed|(?:[^a-z]to|b?cc)\:.*';
if ($message !== true) {
$search .= '|\r|\n';
}
$search = '#(?:' . $search . ')#i';
while (preg_match($search, $value)) {
$value = preg_replace($search, '', $value);
}
return $value;
}
/**
* Wrapper for PHP mail function used for sending out emails
*
* @return bool Success
* @access private
*/
function _mail() {
$header = implode("\r\n", $this->__header);
$message = implode("\r\n", $this->__message);
if (is_array($this->to)) {
$to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
} else {
$to = $this->to;
}
if (ini_get('safe_mode')) {
return @mail($to, $this->_encode($this->subject), $message, $header);
}
return @mail($to, $this->_encode($this->subject), $message, $header, $this->additionalParams);
}
/**
* Sends out email via SMTP
*
* @return bool Success
* @access private
*/
function _smtp() {
App::import('Core', array('CakeSocket'));
$defaults = array(
'host' => 'localhost',
'port' => 25,
'protocol' => 'smtp',
'timeout' => 30
);
$this->smtpOptions = array_merge($defaults, $this->smtpOptions);
$this->__smtpConnection =& new CakeSocket($this->smtpOptions);
if (!$this->__smtpConnection->connect()) {
$this->smtpError = $this->__smtpConnection->lastError();
return false;
} elseif (!$this->_smtpSend(null, '220')) {
return false;
}
$httpHost = env('HTTP_HOST');
if (isset($this->smtpOptions['client'])) {
$host = $this->smtpOptions['client'];
} elseif (!empty($httpHost)) {
$host = $httpHost;
} else {
$host = 'localhost';
}
if (!$this->_smtpSend("EHLO {$host}", '250') && !$this->_smtpSend("HELO {$host}", '250')) {
return false;
}
if (isset($this->smtpOptions['username']) && isset($this->smtpOptions['password'])) {
$authRequired = $this->_smtpSend('AUTH LOGIN', '334|503');
if ($authRequired == '334') {
if (!$this->_smtpSend(base64_encode($this->smtpOptions['username']), '334')) {
return false;
}
if (!$this->_smtpSend(base64_encode($this->smtpOptions['password']), '235')) {
return false;
}
} elseif ($authRequired != '503') {
return false;
}
}
if (!$this->_smtpSend('MAIL FROM: ' . $this->_formatAddress($this->from, true))) {
return false;
}
if (!is_array($this->to)) {
$tos = array($this->to);
} else {
$tos = $this->to;
}
foreach ($tos as $to) {
if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($to, true))) {
return false;
}
}
foreach ($this->cc as $cc) {
if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($cc, true))) {
return false;
}
}
foreach ($this->bcc as $bcc) {
if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($bcc, true))) {
return false;
}
}
if (!$this->_smtpSend('DATA', '354')) {
return false;
}
$header = implode("\r\n", $this->__header);
$message = implode("\r\n", $this->__message);
if (!$this->_smtpSend($header . "\r\n\r\n" . $message . "\r\n\r\n\r\n.")) {
return false;
}
$this->_smtpSend('QUIT', false);
$this->__smtpConnection->disconnect();
return true;
}
/**
* Protected method for sending data to SMTP connection
*
* @param string $data data to be sent to SMTP server
* @param mixed $checkCode code to check for in server response, false to skip
* @return bool Success
* @access protected
*/
function _smtpSend($data, $checkCode = '250') {
if (!is_null($data)) {
$this->__smtpConnection->write($data . "\r\n");
}
while ($checkCode !== false) {
$response = '';
$startTime = time();
while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->smtpOptions['timeout'])) {
$response .= $this->__smtpConnection->read();
}
if (substr($response, -2) !== "\r\n") {
$this->smtpError = 'timeout';
return false;
}
$response = end(explode("\r\n", rtrim($response, "\r\n")));
if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
if ($code[2] === '-') {
continue;
}
return $code[1];
}
$this->smtpError = $response;
return false;
}
return true;
}
/**
* Set as controller flash message a debug message showing current settings in component
*
* @return boolean Success
* @access private
*/
function _debug() {
$nl = "\n";
$header = implode($nl, $this->__header);
$message = implode($nl, $this->__message);
$fm = '<pre>';
if (is_array($this->to)) {
$to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
} else {
$to = $this->to;
}
$fm .= sprintf('%s %s%s', 'To:', $to, $nl);
$fm .= sprintf('%s %s%s', 'From:', $this->from, $nl);
$fm .= sprintf('%s %s%s', 'Subject:', $this->_encode($this->subject), $nl);
$fm .= sprintf('%s%3$s%3$s%s', 'Header:', $header, $nl);
$fm .= sprintf('%s%3$s%3$s%s', 'Parameters:', $this->additionalParams, $nl);
$fm .= sprintf('%s%3$s%3$s%s', 'Message:', $message, $nl);
$fm .= '</pre>';
if (isset($this->Controller->Session)) {
$this->Controller->Session->setFlash($fm, 'default', null, 'email');
return true;
}
return $fm;
}
}

View File

@@ -0,0 +1,826 @@
<?php
/**
* Request object for handling alternative HTTP requests
*
* Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers,
* and the like. These units have no use for Ajax requests, and this Component can tell how Cake
* should respond to the different needs of a handheld computer and a desktop machine.
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.4.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Request object for handling HTTP requests
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1291/Request-Handling
*
*/
class RequestHandlerComponent extends Object {
/**
* The layout that will be switched to for Ajax requests
*
* @var string
* @access public
* @see RequestHandler::setAjax()
*/
var $ajaxLayout = 'ajax';
/**
* Determines whether or not callbacks will be fired on this component
*
* @var boolean
* @access public
*/
var $enabled = true;
/**
* Holds the content-type of the response that is set when using
* RequestHandler::respondAs()
*
* @var string
* @access private
*/
var $__responseTypeSet = null;
/**
* Holds the copy of Controller::$params
*
* @var array
* @access public
*/
var $params = array();
/**
* Friendly content-type mappings used to set response types and determine
* request types. Can be modified with RequestHandler::setContent()
*
* @var array
* @access private
* @see RequestHandlerComponent::setContent
*/
var $__requestContent = array(
'javascript' => 'text/javascript',
'js' => 'text/javascript',
'json' => 'application/json',
'css' => 'text/css',
'html' => array('text/html', '*/*'),
'text' => 'text/plain',
'txt' => 'text/plain',
'csv' => array('application/vnd.ms-excel', 'text/plain'),
'form' => 'application/x-www-form-urlencoded',
'file' => 'multipart/form-data',
'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
'xhtml-mobile' => 'application/vnd.wap.xhtml+xml',
'xml' => array('application/xml', 'text/xml'),
'rss' => 'application/rss+xml',
'atom' => 'application/atom+xml',
'amf' => 'application/x-amf',
'wap' => array(
'text/vnd.wap.wml',
'text/vnd.wap.wmlscript',
'image/vnd.wap.wbmp'
),
'wml' => 'text/vnd.wap.wml',
'wmlscript' => 'text/vnd.wap.wmlscript',
'wbmp' => 'image/vnd.wap.wbmp',
'pdf' => 'application/pdf',
'zip' => 'application/x-zip',
'tar' => 'application/x-tar'
);
/**
* List of regular expressions for matching mobile device's user agent string
*
* @var array
* @access public
*/
var $mobileUA = array(
'Android',
'AvantGo',
'BlackBerry',
'DoCoMo',
'iPod',
'iPhone',
'J2ME',
'MIDP',
'NetFront',
'Nokia',
'Opera Mini',
'PalmOS',
'PalmSource',
'portalmmm',
'Plucker',
'ReqwirelessWeb',
'SonyEricsson',
'Symbian',
'UP\.Browser',
'webOS',
'Windows CE',
'Xiino'
);
/**
* Content-types accepted by the client. If extension parsing is enabled in the
* Router, and an extension is detected, the corresponding content-type will be
* used as the overriding primary content-type accepted.
*
* @var array
* @access private
* @see Router::parseExtensions()
*/
var $__acceptTypes = array();
/**
* The template to use when rendering the given content type.
*
* @var string
* @access private
*/
var $__renderType = null;
/**
* Contains the file extension parsed out by the Router
*
* @var string
* @access public
* @see Router::parseExtensions()
*/
var $ext = null;
/**
* Flag set when MIME types have been initialized
*
* @var boolean
* @access private
* @see RequestHandler::__initializeTypes()
*/
var $__typesInitialized = false;
/**
* Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT
*
*/
function __construct() {
$this->__acceptTypes = explode(',', env('HTTP_ACCEPT'));
foreach ($this->__acceptTypes as $i => $type) {
if (strpos($type, ';')) {
$type = explode(';', $type);
$this->__acceptTypes[$i] = $type[0];
}
}
parent::__construct();
}
/**
* Initializes the component, gets a reference to Controller::$parameters, and
* checks to see if a file extension has been parsed by the Router. If yes, the
* corresponding content-type is pushed onto the list of accepted content-types
* as the first item.
*
* @param object $controller A reference to the controller
* @param array $settings Array of settings to _set().
* @return void
* @see Router::parseExtensions()
* @access public
*/
function initialize(&$controller, $settings = array()) {
if (isset($controller->params['url']['ext'])) {
$this->ext = $controller->params['url']['ext'];
}
$this->_set($settings);
}
/**
* The startup method of the RequestHandler enables several automatic behaviors
* related to the detection of certain properties of the HTTP request, including:
*
* - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header)
* - If Router::parseExtensions() is enabled, the layout and template type are
* switched based on the parsed extension. For example, if controller/action.xml
* is requested, the view path becomes <i>app/views/controller/xml/action.ctp</i>.
* - If a helper with the same name as the extension exists, it is added to the controller.
* - If the extension is of a type that RequestHandler understands, it will set that
* Content-type in the response header.
* - If the XML data is POSTed, the data is parsed into an XML object, which is assigned
* to the $data property of the controller, which can then be saved to a model object.
*
* @param object $controller A reference to the controller
* @return void
* @access public
*/
function startup(&$controller) {
if (!$this->enabled) {
return;
}
$this->__initializeTypes();
$controller->params['isAjax'] = $this->isAjax();
$isRecognized = (
!in_array($this->ext, array('html', 'htm')) &&
in_array($this->ext, array_keys($this->__requestContent))
);
if (!empty($this->ext) && $isRecognized) {
$this->renderAs($controller, $this->ext);
} elseif ($this->isAjax()) {
$this->renderAs($controller, 'ajax');
} elseif (empty($this->ext) || in_array($this->ext, array('html', 'htm'))) {
$this->respondAs('html', array('charset' => Configure::read('App.encoding')));
}
if ($this->requestedWith('xml')) {
if (!class_exists('XmlNode')) {
App::import('Core', 'Xml');
}
$xml = new Xml(trim(file_get_contents('php://input')));
if (count($xml->children) == 1 && is_object($dataNode = $xml->child('data'))) {
$controller->data = $dataNode->toArray();
} else {
$controller->data = $xml->toArray();
}
}
}
/**
* Handles (fakes) redirects for Ajax requests using requestAction()
*
* @param object $controller A reference to the controller
* @param mixed $url A string or array containing the redirect location
* @param mixed HTTP Status for redirect
* @access public
*/
function beforeRedirect(&$controller, $url, $status = null) {
if (!$this->isAjax()) {
return;
}
foreach ($_POST as $key => $val) {
unset($_POST[$key]);
}
if (is_array($url)) {
$url = Router::url($url + array('base' => false));
}
if (!empty($status)) {
$statusCode = $controller->httpCodes($status);
$code = key($statusCode);
$msg = $statusCode[$code];
$controller->header("HTTP/1.1 {$code} {$msg}");
}
echo $this->requestAction($url, array('return', 'bare' => false));
$this->_stop();
}
/**
* Returns true if the current HTTP request is Ajax, false otherwise
*
* @return boolean True if call is Ajax
* @access public
*/
function isAjax() {
return env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest";
}
/**
* Returns true if the current HTTP request is coming from a Flash-based client
*
* @return boolean True if call is from Flash
* @access public
*/
function isFlash() {
return (preg_match('/^(Shockwave|Adobe) Flash/', env('HTTP_USER_AGENT')) == 1);
}
/**
* Returns true if the current request is over HTTPS, false otherwise.
*
* @return bool True if call is over HTTPS
* @access public
*/
function isSSL() {
return env('HTTPS');
}
/**
* Returns true if the current call accepts an XML response, false otherwise
*
* @return boolean True if client accepts an XML response
* @access public
*/
function isXml() {
return $this->prefers('xml');
}
/**
* Returns true if the current call accepts an RSS response, false otherwise
*
* @return boolean True if client accepts an RSS response
* @access public
*/
function isRss() {
return $this->prefers('rss');
}
/**
* Returns true if the current call accepts an Atom response, false otherwise
*
* @return boolean True if client accepts an RSS response
* @access public
*/
function isAtom() {
return $this->prefers('atom');
}
/**
* Returns true if user agent string matches a mobile web browser, or if the
* client accepts WAP content.
*
* @return boolean True if user agent is a mobile web browser
* @access public
* @deprecated Use of constant REQUEST_MOBILE_UA is deprecated and will be removed in future versions
*/
function isMobile() {
if (defined('REQUEST_MOBILE_UA')) {
$regex = '/' . REQUEST_MOBILE_UA . '/i';
} else {
$regex = '/' . implode('|', $this->mobileUA) . '/i';
}
if (preg_match($regex, env('HTTP_USER_AGENT')) || $this->accepts('wap')) {
return true;
}
return false;
}
/**
* Returns true if the client accepts WAP content
*
* @return bool
* @access public
*/
function isWap() {
return $this->prefers('wap');
}
/**
* Returns true if the current call a POST request
*
* @return boolean True if call is a POST
* @access public
*/
function isPost() {
return (strtolower(env('REQUEST_METHOD')) == 'post');
}
/**
* Returns true if the current call a PUT request
*
* @return boolean True if call is a PUT
* @access public
*/
function isPut() {
return (strtolower(env('REQUEST_METHOD')) == 'put');
}
/**
* Returns true if the current call a GET request
*
* @return boolean True if call is a GET
* @access public
*/
function isGet() {
return (strtolower(env('REQUEST_METHOD')) == 'get');
}
/**
* Returns true if the current call a DELETE request
*
* @return boolean True if call is a DELETE
* @access public
*/
function isDelete() {
return (strtolower(env('REQUEST_METHOD')) == 'delete');
}
/**
* Gets Prototype version if call is Ajax, otherwise empty string.
* The Prototype library sets a special "Prototype version" HTTP header.
*
* @return string Prototype version of component making Ajax call
* @access public
*/
function getAjaxVersion() {
if (env('HTTP_X_PROTOTYPE_VERSION') != null) {
return env('HTTP_X_PROTOTYPE_VERSION');
}
return false;
}
/**
* Adds/sets the Content-type(s) for the given name. This method allows
* content-types to be mapped to friendly aliases (or extensions), which allows
* RequestHandler to automatically respond to requests of that type in the
* startup method.
*
* @param string $name The name of the Content-type, i.e. "html", "xml", "css"
* @param mixed $type The Content-type or array of Content-types assigned to the name,
* i.e. "text/html", or "application/xml"
* @return void
* @access public
*/
function setContent($name, $type = null) {
if (is_array($name)) {
$this->__requestContent = array_merge($this->__requestContent, $name);
return;
}
$this->__requestContent[$name] = $type;
}
/**
* Gets the server name from which this request was referred
*
* @return string Server address
* @access public
*/
function getReferer() {
if (env('HTTP_HOST') != null) {
$sessHost = env('HTTP_HOST');
}
if (env('HTTP_X_FORWARDED_HOST') != null) {
$sessHost = env('HTTP_X_FORWARDED_HOST');
}
return trim(preg_replace('/(?:\:.*)/', '', $sessHost));
}
/**
* Gets remote client IP
*
* @return string Client IP address
* @access public
*/
function getClientIP($safe = true) {
if (!$safe && env('HTTP_X_FORWARDED_FOR') != null) {
$ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
} else {
if (env('HTTP_CLIENT_IP') != null) {
$ipaddr = env('HTTP_CLIENT_IP');
} else {
$ipaddr = env('REMOTE_ADDR');
}
}
if (env('HTTP_CLIENTADDRESS') != null) {
$tmpipaddr = env('HTTP_CLIENTADDRESS');
if (!empty($tmpipaddr)) {
$ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
}
}
return trim($ipaddr);
}
/**
* Determines which content types the client accepts. Acceptance is based on
* the file extension parsed by the Router (if present), and by the HTTP_ACCEPT
* header.
*
* @param mixed $type Can be null (or no parameter), a string type name, or an
* array of types
* @return mixed If null or no parameter is passed, returns an array of content
* types the client accepts. If a string is passed, returns true
* if the client accepts it. If an array is passed, returns true
* if the client accepts one or more elements in the array.
* @access public
* @see RequestHandlerComponent::setContent()
*/
function accepts($type = null) {
$this->__initializeTypes();
if ($type == null) {
return $this->mapType($this->__acceptTypes);
} elseif (is_array($type)) {
foreach ($type as $t) {
if ($this->accepts($t) == true) {
return true;
}
}
return false;
} elseif (is_string($type)) {
if (!isset($this->__requestContent[$type])) {
return false;
}
$content = $this->__requestContent[$type];
if (is_array($content)) {
foreach ($content as $c) {
if (in_array($c, $this->__acceptTypes)) {
return true;
}
}
} else {
if (in_array($content, $this->__acceptTypes)) {
return true;
}
}
}
}
/**
* Determines the content type of the data the client has sent (i.e. in a POST request)
*
* @param mixed $type Can be null (or no parameter), a string type name, or an array of types
* @return mixed
* @access public
*/
function requestedWith($type = null) {
if (!$this->isPost() && !$this->isPut()) {
return null;
}
list($contentType) = explode(';', env('CONTENT_TYPE'));
if ($type == null) {
return $this->mapType($contentType);
} elseif (is_array($type)) {
foreach ($type as $t) {
if ($this->requestedWith($t)) {
return $this->mapType($t);
}
}
return false;
} elseif (is_string($type)) {
return ($type == $this->mapType($contentType));
}
}
/**
* Determines which content-types the client prefers. If no parameters are given,
* the content-type that the client most likely prefers is returned. If $type is
* an array, the first item in the array that the client accepts is returned.
* Preference is determined primarily by the file extension parsed by the Router
* if provided, and secondarily by the list of content-types provided in
* HTTP_ACCEPT.
*
* @param mixed $type An optional array of 'friendly' content-type names, i.e.
* 'html', 'xml', 'js', etc.
* @return mixed If $type is null or not provided, the first content-type in the
* list, based on preference, is returned.
* @access public
* @see RequestHandlerComponent::setContent()
*/
function prefers($type = null) {
$this->__initializeTypes();
$accept = $this->accepts();
if ($type == null) {
if (empty($this->ext)) {
if (is_array($accept)) {
return $accept[0];
}
return $accept;
}
return $this->ext;
}
$types = $type;
if (is_string($type)) {
$types = array($type);
}
if (count($types) === 1) {
if (!empty($this->ext)) {
return ($types[0] == $this->ext);
}
return ($types[0] == $accept[0]);
}
$accepts = array();
foreach ($types as $type) {
if (in_array($type, $accept)) {
$accepts[] = $type;
}
}
if (count($accepts) === 0) {
return false;
} elseif (count($types) === 1) {
return ($types[0] === $accepts[0]);
} elseif (count($accepts) === 1) {
return $accepts[0];
}
$acceptedTypes = array();
foreach ($this->__acceptTypes as $type) {
$acceptedTypes[] = $this->mapType($type);
}
$accepts = array_intersect($acceptedTypes, $accepts);
return $accepts[0];
}
/**
* Sets the layout and template paths for the content type defined by $type.
*
* @param object $controller A reference to a controller object
* @param string $type Type of response to send (e.g: 'ajax')
* @return void
* @access public
* @see RequestHandlerComponent::setContent()
* @see RequestHandlerComponent::respondAs()
*/
function renderAs(&$controller, $type) {
$this->__initializeTypes();
$options = array('charset' => 'UTF-8');
if (Configure::read('App.encoding') !== null) {
$options = array('charset' => Configure::read('App.encoding'));
}
if ($type == 'ajax') {
$controller->layout = $this->ajaxLayout;
return $this->respondAs('html', $options);
}
$controller->ext = '.ctp';
if (empty($this->__renderType)) {
$controller->viewPath .= DS . $type;
} else {
$remove = preg_replace("/([\/\\\\]{$this->__renderType})$/", DS . $type, $controller->viewPath);
$controller->viewPath = $remove;
}
$this->__renderType = $type;
$controller->layoutPath = $type;
if (isset($this->__requestContent[$type])) {
$this->respondAs($type, $options);
}
$helper = ucfirst($type);
$isAdded = (
in_array($helper, $controller->helpers) ||
array_key_exists($helper, $controller->helpers)
);
if (!$isAdded) {
if (App::import('Helper', $helper)) {
$controller->helpers[] = $helper;
}
}
}
/**
* Sets the response header based on type map index name. If DEBUG is greater than 2, the header
* is not set.
*
* @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
* like 'application/x-shockwave'.
* @param array $options If $type is a friendly type name that is associated with
* more than one type of content, $index is used to select which content-type to use.
* @return boolean Returns false if the friendly type name given in $type does
* not exist in the type map, or if the Content-type header has
* already been set by this method.
* @access public
* @see RequestHandlerComponent::setContent()
*/
function respondAs($type, $options = array()) {
$this->__initializeTypes();
if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) {
return false;
}
$defaults = array('index' => 0, 'charset' => null, 'attachment' => false);
$options = array_merge($defaults, $options);
if (strpos($type, '/') === false && isset($this->__requestContent[$type])) {
$cType = null;
if (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][$options['index']])) {
$cType = $this->__requestContent[$type][$options['index']];
} elseif (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][0])) {
$cType = $this->__requestContent[$type][0];
} elseif (isset($this->__requestContent[$type])) {
$cType = $this->__requestContent[$type];
} else {
return false;
}
if (is_array($cType)) {
if ($this->prefers($cType)) {
$cType = $this->prefers($cType);
} else {
$cType = $cType[0];
}
}
} else {
$cType = $type;
}
if ($cType != null) {
$header = 'Content-type: ' . $cType;
if (!empty($options['charset'])) {
$header .= '; charset=' . $options['charset'];
}
if (!empty($options['attachment'])) {
$this->_header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
}
if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) {
$this->_header($header);
}
$this->__responseTypeSet = $cType;
return true;
}
return false;
}
/**
* Wrapper for header() so calls can be easily tested.
*
* @param string $header The header to be sent.
* @return void
* @access protected
*/
function _header($header) {
header($header);
}
/**
* Returns the current response type (Content-type header), or null if none has been set
*
* @return mixed A string content type alias, or raw content type if no alias map exists,
* otherwise null
* @access public
*/
function responseType() {
if ($this->__responseTypeSet == null) {
return null;
}
return $this->mapType($this->__responseTypeSet);
}
/**
* Maps a content-type back to an alias
*
* @param mixed $type Content type
* @return mixed Alias
* @access public
*/
function mapType($ctype) {
if (is_array($ctype)) {
$out = array();
foreach ($ctype as $t) {
$out[] = $this->mapType($t);
}
return $out;
} else {
$keys = array_keys($this->__requestContent);
$count = count($keys);
for ($i = 0; $i < $count; $i++) {
$name = $keys[$i];
$type = $this->__requestContent[$name];
if (is_array($type) && in_array($ctype, $type)) {
return $name;
} elseif (!is_array($type) && $type == $ctype) {
return $name;
}
}
return $ctype;
}
}
/**
* Initializes MIME types
*
* @return void
* @access private
*/
function __initializeTypes() {
if ($this->__typesInitialized) {
return;
}
if (isset($this->__requestContent[$this->ext])) {
$content = $this->__requestContent[$this->ext];
if (is_array($content)) {
$content = $content[0];
}
array_unshift($this->__acceptTypes, $content);
}
$this->__typesInitialized = true;
}
}

View File

@@ -0,0 +1,751 @@
<?php
/**
* Security Component
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.8.2156
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', array('String', 'Security'));
/**
* SecurityComponent
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1296/Security-Component
*/
class SecurityComponent extends Object {
/**
* The controller method that will be called if this request is black-hole'd
*
* @var string
* @access public
*/
var $blackHoleCallback = null;
/**
* List of controller actions for which a POST request is required
*
* @var array
* @access public
* @see SecurityComponent::requirePost()
*/
var $requirePost = array();
/**
* List of controller actions for which a GET request is required
*
* @var array
* @access public
* @see SecurityComponent::requireGet()
*/
var $requireGet = array();
/**
* List of controller actions for which a PUT request is required
*
* @var array
* @access public
* @see SecurityComponent::requirePut()
*/
var $requirePut = array();
/**
* List of controller actions for which a DELETE request is required
*
* @var array
* @access public
* @see SecurityComponent::requireDelete()
*/
var $requireDelete = array();
/**
* List of actions that require an SSL-secured connection
*
* @var array
* @access public
* @see SecurityComponent::requireSecure()
*/
var $requireSecure = array();
/**
* List of actions that require a valid authentication key
*
* @var array
* @access public
* @see SecurityComponent::requireAuth()
*/
var $requireAuth = array();
/**
* List of actions that require an HTTP-authenticated login (basic or digest)
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
var $requireLogin = array();
/**
* Login options for SecurityComponent::requireLogin()
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
var $loginOptions = array('type' => '', 'prompt' => null);
/**
* An associative array of usernames/passwords used for HTTP-authenticated logins.
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
var $loginUsers = array();
/**
* Controllers from which actions of the current controller are allowed to receive
* requests.
*
* @var array
* @access public
* @see SecurityComponent::requireAuth()
*/
var $allowedControllers = array();
/**
* Actions from which actions of the current controller are allowed to receive
* requests.
*
* @var array
* @access public
* @see SecurityComponent::requireAuth()
*/
var $allowedActions = array();
/**
* Form fields to disable
*
* @var array
* @access public
*/
var $disabledFields = array();
/**
* Whether to validate POST data. Set to false to disable for data coming from 3rd party
* services, etc.
*
* @var boolean
* @access public
*/
var $validatePost = true;
/**
* Other components used by the Security component
*
* @var array
* @access public
*/
var $components = array('RequestHandler', 'Session');
/**
* Holds the current action of the controller
*
* @var string
*/
var $_action = null;
/**
* Initialize the SecurityComponent
*
* @param object $controller Controller instance for the request
* @param array $settings Settings to set to the component
* @return void
* @access public
*/
function initialize(&$controller, $settings = array()) {
$this->_set($settings);
}
/**
* Component startup. All security checking happens here.
*
* @param object $controller Instantiating controller
* @return void
* @access public
*/
function startup(&$controller) {
$this->_action = strtolower($controller->action);
$this->_methodsRequired($controller);
$this->_secureRequired($controller);
$this->_authRequired($controller);
$this->_loginRequired($controller);
$isPost = ($this->RequestHandler->isPost() || $this->RequestHandler->isPut());
$isRequestAction = (
!isset($controller->params['requested']) ||
$controller->params['requested'] != 1
);
if ($isPost && $isRequestAction && $this->validatePost) {
if ($this->_validatePost($controller) === false) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
}
$this->_generateToken($controller);
}
/**
* Sets the actions that require a POST request, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1299/requirePost
*/
function requirePost() {
$args = func_get_args();
$this->_requireMethod('Post', $args);
}
/**
* Sets the actions that require a GET request, or empty for all actions
*
* @return void
* @access public
*/
function requireGet() {
$args = func_get_args();
$this->_requireMethod('Get', $args);
}
/**
* Sets the actions that require a PUT request, or empty for all actions
*
* @return void
* @access public
*/
function requirePut() {
$args = func_get_args();
$this->_requireMethod('Put', $args);
}
/**
* Sets the actions that require a DELETE request, or empty for all actions
*
* @return void
* @access public
*/
function requireDelete() {
$args = func_get_args();
$this->_requireMethod('Delete', $args);
}
/**
* Sets the actions that require a request that is SSL-secured, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1300/requireSecure
*/
function requireSecure() {
$args = func_get_args();
$this->_requireMethod('Secure', $args);
}
/**
* Sets the actions that require an authenticated request, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1301/requireAuth
*/
function requireAuth() {
$args = func_get_args();
$this->_requireMethod('Auth', $args);
}
/**
* Sets the actions that require an HTTP-authenticated request, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1302/requireLogin
*/
function requireLogin() {
$args = func_get_args();
$base = $this->loginOptions;
foreach ($args as $i => $arg) {
if (is_array($arg)) {
$this->loginOptions = $arg;
unset($args[$i]);
}
}
$this->loginOptions = array_merge($base, $this->loginOptions);
$this->_requireMethod('Login', $args);
if (isset($this->loginOptions['users'])) {
$this->loginUsers =& $this->loginOptions['users'];
}
}
/**
* Attempts to validate the login credentials for an HTTP-authenticated request
*
* @param string $type Either 'basic', 'digest', or null. If null/empty, will try both.
* @return mixed If successful, returns an array with login name and password, otherwise null.
* @access public
* @link http://book.cakephp.org/view/1303/loginCredentials-string-type
*/
function loginCredentials($type = null) {
switch (strtolower($type)) {
case 'basic':
$login = array('username' => env('PHP_AUTH_USER'), 'password' => env('PHP_AUTH_PW'));
if (!empty($login['username'])) {
return $login;
}
break;
case 'digest':
default:
$digest = null;
if (version_compare(PHP_VERSION, '5.1') != -1) {
$digest = env('PHP_AUTH_DIGEST');
} elseif (function_exists('apache_request_headers')) {
$headers = apache_request_headers();
if (isset($headers['Authorization']) && !empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') {
$digest = substr($headers['Authorization'], 7);
}
} else {
// Server doesn't support digest-auth headers
trigger_error(__('SecurityComponent::loginCredentials() - Server does not support digest authentication', true), E_USER_WARNING);
}
if (!empty($digest)) {
return $this->parseDigestAuthData($digest);
}
break;
}
return null;
}
/**
* Generates the text of an HTTP-authentication request header from an array of options.
*
* @param array $options Set of options for header
* @return string HTTP-authentication request header
* @access public
* @link http://book.cakephp.org/view/1304/loginRequest-array-options
*/
function loginRequest($options = array()) {
$options = array_merge($this->loginOptions, $options);
$this->_setLoginDefaults($options);
$auth = 'WWW-Authenticate: ' . ucfirst($options['type']);
$out = array('realm="' . $options['realm'] . '"');
if (strtolower($options['type']) == 'digest') {
$out[] = 'qop="auth"';
$out[] = 'nonce="' . uniqid("") . '"';
$out[] = 'opaque="' . md5($options['realm']) . '"';
}
return $auth . ' ' . implode(',', $out);
}
/**
* Parses an HTTP digest authentication response, and returns an array of the data, or null on failure.
*
* @param string $digest Digest authentication response
* @return array Digest authentication parameters
* @access public
* @link http://book.cakephp.org/view/1305/parseDigestAuthData-string-digest
*/
function parseDigestAuthData($digest) {
if (substr($digest, 0, 7) == 'Digest ') {
$digest = substr($digest, 7);
}
$keys = array();
$match = array();
$req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER);
foreach ($match as $i) {
$keys[$i[1]] = $i[3];
unset($req[$i[1]]);
}
if (empty($req)) {
return $keys;
}
return null;
}
/**
* Generates a hash to be compared with an HTTP digest-authenticated response
*
* @param array $data HTTP digest response data, as parsed by SecurityComponent::parseDigestAuthData()
* @return string Digest authentication hash
* @access public
* @see SecurityComponent::parseDigestAuthData()
* @link http://book.cakephp.org/view/1306/generateDigestResponseHash-array-data
*/
function generateDigestResponseHash($data) {
return md5(
md5($data['username'] . ':' . $this->loginOptions['realm'] . ':' . $this->loginUsers[$data['username']]) .
':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
md5(env('REQUEST_METHOD') . ':' . $data['uri'])
);
}
/**
* Black-hole an invalid request with a 404 error or custom callback. If SecurityComponent::$blackHoleCallback
* is specified, it will use this callback by executing the method indicated in $error
*
* @param object $controller Instantiating controller
* @param string $error Error method
* @return mixed If specified, controller blackHoleCallback's response, or no return otherwise
* @access public
* @see SecurityComponent::$blackHoleCallback
* @link http://book.cakephp.org/view/1307/blackHole-object-controller-string-error
*/
function blackHole(&$controller, $error = '') {
if ($this->blackHoleCallback == null) {
$code = 404;
if ($error == 'login') {
$code = 401;
$controller->header($this->loginRequest());
}
$controller->redirect(null, $code, true);
} else {
return $this->_callback($controller, $this->blackHoleCallback, array($error));
}
}
/**
* Sets the actions that require a $method HTTP request, or empty for all actions
*
* @param string $method The HTTP method to assign controller actions to
* @param array $actions Controller actions to set the required HTTP method to.
* @return void
* @access protected
*/
function _requireMethod($method, $actions = array()) {
if (isset($actions[0]) && is_array($actions[0])) {
$actions = $actions[0];
}
$this->{'require' . $method} = (empty($actions)) ? array('*'): $actions;
}
/**
* Check if HTTP methods are required
*
* @param object $controller Instantiating controller
* @return bool true if $method is required
* @access protected
*/
function _methodsRequired(&$controller) {
foreach (array('Post', 'Get', 'Put', 'Delete') as $method) {
$property = 'require' . $method;
if (is_array($this->$property) && !empty($this->$property)) {
$require = array_map('strtolower', $this->$property);
if (in_array($this->_action, $require) || $this->$property == array('*')) {
if (!$this->RequestHandler->{'is' . $method}()) {
if (!$this->blackHole($controller, strtolower($method))) {
return null;
}
}
}
}
}
return true;
}
/**
* Check if access requires secure connection
*
* @param object $controller Instantiating controller
* @return bool true if secure connection required
* @access protected
*/
function _secureRequired(&$controller) {
if (is_array($this->requireSecure) && !empty($this->requireSecure)) {
$requireSecure = array_map('strtolower', $this->requireSecure);
if (in_array($this->_action, $requireSecure) || $this->requireSecure == array('*')) {
if (!$this->RequestHandler->isSSL()) {
if (!$this->blackHole($controller, 'secure')) {
return null;
}
}
}
}
return true;
}
/**
* Check if authentication is required
*
* @param object $controller Instantiating controller
* @return bool true if authentication required
* @access protected
*/
function _authRequired(&$controller) {
if (is_array($this->requireAuth) && !empty($this->requireAuth) && !empty($controller->data)) {
$requireAuth = array_map('strtolower', $this->requireAuth);
if (in_array($this->_action, $requireAuth) || $this->requireAuth == array('*')) {
if (!isset($controller->data['_Token'] )) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
if ($this->Session->check('_Token')) {
$tData = unserialize($this->Session->read('_Token'));
if (!empty($tData['allowedControllers']) && !in_array($controller->params['controller'], $tData['allowedControllers']) || !empty($tData['allowedActions']) && !in_array($controller->params['action'], $tData['allowedActions'])) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
} else {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
}
}
return true;
}
/**
* Check if login is required
*
* @param object $controller Instantiating controller
* @return bool true if login is required
* @access protected
*/
function _loginRequired(&$controller) {
if (is_array($this->requireLogin) && !empty($this->requireLogin)) {
$requireLogin = array_map('strtolower', $this->requireLogin);
if (in_array($this->_action, $requireLogin) || $this->requireLogin == array('*')) {
$login = $this->loginCredentials($this->loginOptions['type']);
if ($login == null) {
$controller->header($this->loginRequest());
if (!empty($this->loginOptions['prompt'])) {
$this->_callback($controller, $this->loginOptions['prompt']);
} else {
$this->blackHole($controller, 'login');
}
} else {
if (isset($this->loginOptions['login'])) {
$this->_callback($controller, $this->loginOptions['login'], array($login));
} else {
if (strtolower($this->loginOptions['type']) == 'digest') {
if ($login && isset($this->loginUsers[$login['username']])) {
if ($login['response'] == $this->generateDigestResponseHash($login)) {
return true;
}
}
$this->blackHole($controller, 'login');
} else {
if (
!(in_array($login['username'], array_keys($this->loginUsers)) &&
$this->loginUsers[$login['username']] == $login['password'])
) {
$this->blackHole($controller, 'login');
}
}
}
}
}
}
return true;
}
/**
* Validate submitted form
*
* @param object $controller Instantiating controller
* @return bool true if submitted form is valid
* @access protected
*/
function _validatePost(&$controller) {
if (empty($controller->data)) {
return true;
}
$data = $controller->data;
if (!isset($data['_Token']) || !isset($data['_Token']['fields']) || !isset($data['_Token']['key'])) {
return false;
}
$token = $data['_Token']['key'];
if ($this->Session->check('_Token')) {
$tokenData = unserialize($this->Session->read('_Token'));
if ($tokenData['expires'] < time() || $tokenData['key'] !== $token) {
return false;
}
}
$locked = null;
$check = $controller->data;
$token = urldecode($check['_Token']['fields']);
if (strpos($token, ':')) {
list($token, $locked) = explode(':', $token, 2);
}
unset($check['_Token']);
$locked = str_rot13($locked);
if (preg_match('/(\A|;|{|})O\:[0-9]+/', $locked)) {
return false;
}
$lockedFields = array();
$fields = Set::flatten($check);
$fieldList = array_keys($fields);
$locked = unserialize($locked);
$multi = array();
foreach ($fieldList as $i => $key) {
if (preg_match('/\.\d+$/', $key)) {
$multi[$i] = preg_replace('/\.\d+$/', '', $key);
unset($fieldList[$i]);
}
}
if (!empty($multi)) {
$fieldList += array_unique($multi);
}
foreach ($fieldList as $i => $key) {
$isDisabled = false;
$isLocked = (is_array($locked) && in_array($key, $locked));
if (!empty($this->disabledFields)) {
foreach ((array)$this->disabledFields as $disabled) {
$disabled = explode('.', $disabled);
$field = array_values(array_intersect(explode('.', $key), $disabled));
$isDisabled = ($field === $disabled);
if ($isDisabled) {
break;
}
}
}
if ($isDisabled || $isLocked) {
unset($fieldList[$i]);
if ($isLocked) {
$lockedFields[$key] = $fields[$key];
}
}
}
sort($fieldList, SORT_STRING);
ksort($lockedFields, SORT_STRING);
$fieldList += $lockedFields;
$check = Security::hash(serialize($fieldList) . Configure::read('Security.salt'));
return ($token === $check);
}
/**
* Add authentication key for new form posts
*
* @param object $controller Instantiating controller
* @return bool Success
* @access protected
*/
function _generateToken(&$controller) {
if (isset($controller->params['requested']) && $controller->params['requested'] === 1) {
if ($this->Session->check('_Token')) {
$tokenData = unserialize($this->Session->read('_Token'));
$controller->params['_Token'] = $tokenData;
}
return false;
}
$authKey = Security::generateAuthKey();
$expires = strtotime('+' . Security::inactiveMins() . ' minutes');
$token = array(
'key' => $authKey,
'expires' => $expires,
'allowedControllers' => $this->allowedControllers,
'allowedActions' => $this->allowedActions,
'disabledFields' => $this->disabledFields
);
if (!isset($controller->data)) {
$controller->data = array();
}
if ($this->Session->check('_Token')) {
$tokenData = unserialize($this->Session->read('_Token'));
$valid = (
isset($tokenData['expires']) &&
$tokenData['expires'] > time() &&
isset($tokenData['key'])
);
if ($valid) {
$token['key'] = $tokenData['key'];
}
}
$controller->params['_Token'] = $token;
$this->Session->write('_Token', serialize($token));
return true;
}
/**
* Sets the default login options for an HTTP-authenticated request
*
* @param array $options Default login options
* @return void
* @access protected
*/
function _setLoginDefaults(&$options) {
$options = array_merge(array(
'type' => 'basic',
'realm' => env('SERVER_NAME'),
'qop' => 'auth',
'nonce' => String::uuid()
), array_filter($options));
$options = array_merge(array('opaque' => md5($options['realm'])), $options);
}
/**
* Calls a controller callback method
*
* @param object $controller Controller to run callback on
* @param string $method Method to execute
* @param array $params Parameters to send to method
* @return mixed Controller callback method's response
* @access protected
*/
function _callback(&$controller, $method, $params = array()) {
if (is_callable(array($controller, $method))) {
return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params);
} else {
return null;
}
}
}

View File

@@ -0,0 +1,295 @@
<?php
/**
* SessionComponent. Provides access to Sessions from the Controller layer
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.0.1232
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
if (!class_exists('cakesession')) {
require LIBS . 'cake_session.php';
}
/**
* Session Component.
*
* Session handling from the controller.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1310/Sessions
*
*/
class SessionComponent extends CakeSession {
/**
* Used to determine if methods implementation is used, or bypassed
*
* @var boolean
* @access private
*/
var $__active = true;
/**
* Used to determine if request are from an Ajax request
*
* @var boolean
* @access private
*/
var $__bare = 0;
/**
* Class constructor
*
* @param string $base The base path for the Session
*/
function __construct($base = null) {
if (Configure::read('Session.start') === true) {
parent::__construct($base);
} else {
$this->__active = false;
}
}
/**
* Startup method.
*
* @param object $controller Instantiating controller
* @return void
* @access public
*/
function startup(&$controller) {
if ($this->started() === false && $this->__active === true) {
$this->__start();
}
}
/**
* Starts Session on if 'Session.start' is set to false in core.php
*
* @param string $base The base path for the Session
* @return void
* @access public
*/
function activate($base = null) {
if ($this->__active === true) {
return;
}
parent::__construct($base);
$this->__active = true;
}
/**
* Used to write a value to a session key.
*
* In your controller: $this->Session->write('Controller.sessKey', 'session value');
*
* @param string $name The name of the key your are setting in the session.
* This should be in a Controller.key format for better organizing
* @param string $value The value you want to store in a session.
* @return boolean Success
* @access public
* @link http://book.cakephp.org/view/1312/write
*/
function write($name, $value = null) {
if ($this->__active === true) {
$this->__start();
if (is_array($name)) {
foreach ($name as $key => $value) {
if (parent::write($key, $value) === false) {
return false;
}
}
return true;
}
if (parent::write($name, $value) === false) {
return false;
}
return true;
}
return false;
}
/**
* Used to read a session values for a key or return values for all keys.
*
* In your controller: $this->Session->read('Controller.sessKey');
* Calling the method without a param will return all session vars
*
* @param string $name the name of the session key you want to read
* @return mixed value from the session vars
* @access public
* @link http://book.cakephp.org/view/1314/read
*/
function read($name = null) {
if ($this->__active === true) {
$this->__start();
return parent::read($name);
}
return false;
}
/**
* Wrapper for SessionComponent::del();
*
* In your controller: $this->Session->delete('Controller.sessKey');
*
* @param string $name the name of the session key you want to delete
* @return boolean true is session variable is set and can be deleted, false is variable was not set.
* @access public
* @link http://book.cakephp.org/view/1316/delete
*/
function delete($name) {
if ($this->__active === true) {
$this->__start();
return parent::delete($name);
}
return false;
}
/**
* Used to check if a session variable is set
*
* In your controller: $this->Session->check('Controller.sessKey');
*
* @param string $name the name of the session key you want to check
* @return boolean true is session variable is set, false if not
* @access public
* @link http://book.cakephp.org/view/1315/check
*/
function check($name) {
if ($this->__active === true) {
$this->__start();
return parent::check($name);
}
return false;
}
/**
* Used to determine the last error in a session.
*
* In your controller: $this->Session->error();
*
* @return string Last session error
* @access public
* @link http://book.cakephp.org/view/1318/error
*/
function error() {
if ($this->__active === true) {
$this->__start();
return parent::error();
}
return false;
}
/**
* Used to set a session variable that can be used to output messages in the view.
*
* In your controller: $this->Session->setFlash('This has been saved');
*
* Additional params below can be passed to customize the output, or the Message.[key]
*
* @param string $message Message to be flashed
* @param string $element Element to wrap flash message in.
* @param array $params Parameters to be sent to layout as view variables
* @param string $key Message key, default is 'flash'
* @access public
* @link http://book.cakephp.org/view/1313/setFlash
*/
function setFlash($message, $element = 'default', $params = array(), $key = 'flash') {
if ($this->__active === true) {
$this->__start();
$this->write('Message.' . $key, compact('message', 'element', 'params'));
}
}
/**
* Used to renew a session id
*
* In your controller: $this->Session->renew();
*
* @return void
* @access public
*/
function renew() {
if ($this->__active === true) {
$this->__start();
parent::renew();
}
}
/**
* Used to check for a valid session.
*
* In your controller: $this->Session->valid();
*
* @return boolean true is session is valid, false is session is invalid
* @access public
*/
function valid() {
if ($this->__active === true) {
$this->__start();
return parent::valid();
}
return false;
}
/**
* Used to destroy sessions
*
* In your controller: $this->Session->destroy();
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1317/destroy
*/
function destroy() {
if ($this->__active === true) {
$this->__start();
parent::destroy();
}
}
/**
* Returns Session id
*
* If $id is passed in a beforeFilter, the Session will be started
* with the specified id
*
* @param $id string
* @return string
* @access public
*/
function id($id = null) {
return parent::id($id);
}
/**
* Starts Session if SessionComponent is used in Controller::beforeFilter(),
* or is called from
*
* @return boolean
* @access private
*/
function __start() {
if ($this->started() === false) {
if (!$this->id() && parent::start()) {
parent::_checkValid();
} else {
parent::start();
}
}
return $this->started();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
<?php
/**
* Static content controller.
*
* This file will render views from views/pages/
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Static content controller
*
* Override this controller by placing a copy in controllers directory of an application
*
* @package cake
* @subpackage cake.cake.libs.controller
* @link http://book.cakephp.org/view/958/The-Pages-Controller
*/
class PagesController extends AppController {
/**
* Controller name
*
* @var string
* @access public
*/
var $name = 'Pages';
/**
* Default helper
*
* @var array
* @access public
*/
var $helpers = array('Html', 'Session');
/**
* This controller does not use a model
*
* @var array
* @access public
*/
var $uses = array();
/**
* Displays a view
*
* @param mixed What page to display
* @access public
*/
function display() {
$path = func_get_args();
$count = count($path);
if (!$count) {
$this->redirect('/');
}
$page = $subpage = $title_for_layout = null;
if (!empty($path[0])) {
$page = $path[0];
}
if (!empty($path[1])) {
$subpage = $path[1];
}
if (!empty($path[$count - 1])) {
$title_for_layout = Inflector::humanize($path[$count - 1]);
}
$this->set(compact('page', 'subpage', 'title_for_layout'));
$this->render(implode('/', $path));
}
}

View File

@@ -0,0 +1,613 @@
<?php
/**
* Scaffold.
*
* Automatic forms and actions generation for rapid web application development.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since Cake v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Scaffolding is a set of automatic actions for starting web development work faster.
*
* Scaffold inspects your database tables, and making educated guesses, sets up a
* number of pages for each of your Models. These pages have data forms that work,
* and afford the web developer an early look at the data, and the possibility to over-ride
* scaffolded actions with custom-made ones.
*
* @package cake
* @subpackage cake.cake.libs.controller
*/
class Scaffold extends Object {
/**
* Controller object
*
* @var Controller
* @access public
*/
var $controller = null;
/**
* Name of the controller to scaffold
*
* @var string
* @access public
*/
var $name = null;
/**
* Action to be performed.
*
* @var string
* @access public
*/
var $action = null;
/**
* Name of current model this view context is attached to
*
* @var string
* @access public
*/
var $model = null;
/**
* Path to View.
*
* @var string
* @access public
*/
var $viewPath;
/**
* Path parts for creating links in views.
*
* @var string Base URL
* @access public
*/
var $base = null;
/**
* Name of layout to use with this View.
*
* @var string
* @access public
*/
var $layout = 'default';
/**
* Array of parameter data
*
* @var array
* @access public
*/
var $params;
/**
* File extension. Defaults to Cake's template ".ctp".
*
* @var array
* @access public
*/
var $ext = '.ctp';
/**
* Sub-directory for this view file.
*
* @var string
* @access public
*/
var $subDir = null;
/**
* Plugin name.
*
* @var string
* @access public
*/
var $plugin = null;
/**
* valid session.
*
* @var boolean
* @access public
*/
var $_validSession = null;
/**
* List of variables to collect from the associated controller
*
* @var array
* @access private
*/
var $__passedVars = array(
'action', 'base', 'webroot', 'layout', 'name',
'viewPath', 'ext', 'params', 'data', 'plugin', 'cacheAction'
);
/**
* Title HTML element for current scaffolded view
*
* @var string
* @access public
*/
var $scaffoldTitle = null;
/**
* Construct and set up given controller with given parameters.
*
* @param string $controller_class Name of controller
* @param array $params Parameters for scaffolding
*/
function __construct(&$controller, $params) {
$this->controller =& $controller;
$count = count($this->__passedVars);
for ($j = 0; $j < $count; $j++) {
$var = $this->__passedVars[$j];
$this->{$var} = $controller->{$var};
}
$this->redirect = array('action' => 'index');
$this->modelClass = $controller->modelClass;
$this->modelKey = $controller->modelKey;
if (!is_object($this->controller->{$this->modelClass})) {
return $this->cakeError('missingModel', array(array(
'className' => $this->modelClass, 'webroot' => '', 'base' => $controller->base
)));
}
$this->ScaffoldModel =& $this->controller->{$this->modelClass};
$this->scaffoldTitle = Inflector::humanize($this->viewPath);
$this->scaffoldActions = $controller->scaffold;
$title_for_layout = __('Scaffold :: ', true) . Inflector::humanize($this->action) . ' :: ' . $this->scaffoldTitle;
$modelClass = $this->controller->modelClass;
$primaryKey = $this->ScaffoldModel->primaryKey;
$displayField = $this->ScaffoldModel->displayField;
$singularVar = Inflector::variable($modelClass);
$pluralVar = Inflector::variable($this->controller->name);
$singularHumanName = Inflector::humanize(Inflector::underscore($modelClass));
$pluralHumanName = Inflector::humanize(Inflector::underscore($this->controller->name));
$scaffoldFields = array_keys($this->ScaffoldModel->schema());
$associations = $this->__associations();
$this->controller->set(compact(
'title_for_layout', 'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
'singularHumanName', 'pluralHumanName', 'scaffoldFields', 'associations'
));
if ($this->controller->view) {
$this->controller->view = 'Scaffold';
}
$this->_validSession = (
isset($this->controller->Session) && $this->controller->Session->valid() != false
);
$this->__scaffold($params);
}
/**
* Outputs the content of a scaffold method passing it through the Controller::afterFilter()
*
* @return void
* @access protected
*/
function _output() {
$this->controller->afterFilter();
echo($this->controller->output);
}
/**
* Renders a view action of scaffolded model.
*
* @param array $params Parameters for scaffolding
* @return mixed A rendered view of a row from Models database table
* @access private
*/
function __scaffoldView($params) {
if ($this->controller->_beforeScaffold('view')) {
$message = __(sprintf("No id set for %s::view()", Inflector::humanize($this->modelKey)), true);
if (isset($params['pass'][0])) {
$this->ScaffoldModel->id = $params['pass'][0];
} elseif ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
return $this->controller->flash($message, '/' . Inflector::underscore($this->controller->viewPath));
}
$this->ScaffoldModel->recursive = 1;
$this->controller->data = $this->ScaffoldModel->read();
$this->controller->set(
Inflector::variable($this->controller->modelClass), $this->controller->data
);
$this->controller->render($this->action, $this->layout);
$this->_output();
} elseif ($this->controller->_scaffoldError('view') === false) {
return $this->__scaffoldError();
}
}
/**
* Renders index action of scaffolded model.
*
* @param array $params Parameters for scaffolding
* @return mixed A rendered view listing rows from Models database table
* @access private
*/
function __scaffoldIndex($params) {
if ($this->controller->_beforeScaffold('index')) {
$this->ScaffoldModel->recursive = 0;
$this->controller->set(
Inflector::variable($this->controller->name), $this->controller->paginate()
);
$this->controller->render($this->action, $this->layout);
$this->_output();
} elseif ($this->controller->_scaffoldError('index') === false) {
return $this->__scaffoldError();
}
}
/**
* Renders an add or edit action for scaffolded model.
*
* @param string $action Action (add or edit)
* @return mixed A rendered view with a form to edit or add a record in the Models database table
* @access private
*/
function __scaffoldForm($action = 'edit') {
$this->controller->viewVars['scaffoldFields'] = array_merge(
$this->controller->viewVars['scaffoldFields'],
array_keys($this->ScaffoldModel->hasAndBelongsToMany)
);
$this->controller->render($action, $this->layout);
$this->_output();
}
/**
* Saves or updates the scaffolded model.
*
* @param array $params Parameters for scaffolding
* @param string $action add or edt
* @return mixed Success on save/update, add/edit form if data is empty or error if save or update fails
* @access private
*/
function __scaffoldSave($params = array(), $action = 'edit') {
$formAction = 'edit';
$success = __('updated', true);
if ($action === 'add') {
$formAction = 'add';
$success = __('saved', true);
}
if ($this->controller->_beforeScaffold($action)) {
if ($action == 'edit') {
if (isset($params['pass'][0])) {
$this->ScaffoldModel->id = $params['pass'][0];
}
if (!$this->ScaffoldModel->exists()) {
$message = __(sprintf("Invalid id for %s::edit()", Inflector::humanize($this->modelKey), true));
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
$this->_output();
}
}
}
if (!empty($this->controller->data)) {
if ($action == 'create') {
$this->ScaffoldModel->create();
}
if ($this->ScaffoldModel->save($this->controller->data)) {
if ($this->controller->_afterScaffoldSave($action)) {
$message = __(
sprintf('The %1$s has been %2$s', Inflector::humanize($this->modelKey), $success),
true
);
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
} else {
return $this->controller->_afterScaffoldSaveError($action);
}
} else {
if ($this->_validSession) {
$this->controller->Session->setFlash(__('Please correct errors below.', true));
}
}
}
if (empty($this->controller->data)) {
if ($this->ScaffoldModel->id) {
$this->controller->data = $this->ScaffoldModel->read();
} else {
$this->controller->data = $this->ScaffoldModel->create();
}
}
foreach ($this->ScaffoldModel->belongsTo as $assocName => $assocData) {
$varName = Inflector::variable(Inflector::pluralize(
preg_replace('/(?:_id)$/', '', $assocData['foreignKey'])
));
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
}
foreach ($this->ScaffoldModel->hasAndBelongsToMany as $assocName => $assocData) {
$varName = Inflector::variable(Inflector::pluralize($assocName));
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
}
return $this->__scaffoldForm($formAction);
} elseif ($this->controller->_scaffoldError($action) === false) {
return $this->__scaffoldError();
}
}
/**
* Performs a delete on given scaffolded Model.
*
* @param array $params Parameters for scaffolding
* @return mixed Success on delete, error if delete fails
* @access private
*/
function __scaffoldDelete($params = array()) {
if ($this->controller->_beforeScaffold('delete')) {
$message = __(
sprintf("No id set for %s::delete()", Inflector::humanize($this->modelKey)), true
);
if (isset($params['pass'][0])) {
$id = $params['pass'][0];
} elseif ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
if ($this->ScaffoldModel->delete($id)) {
$message = __(
sprintf('The %1$s with id: %2$d has been deleted.', Inflector::humanize($this->modelClass), $id),
true
);
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
} else {
$message = __(sprintf(
'There was an error deleting the %1$s with id: %2$d',
Inflector::humanize($this->modelClass), $id
), true);
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
}
} elseif ($this->controller->_scaffoldError('delete') === false) {
return $this->__scaffoldError();
}
}
/**
* Show a scaffold error
*
* @return mixed A rendered view showing the error
* @access private
*/
function __scaffoldError() {
return $this->controller->render('error', $this->layout);
$this->_output();
}
/**
* When methods are now present in a controller
* scaffoldView is used to call default Scaffold methods if:
* `var $scaffold;` is placed in the controller's class definition.
*
* @param array $params Parameters for scaffolding
* @return mixed A rendered view of scaffold action, or showing the error
* @access private
*/
function __scaffold($params) {
$db = &ConnectionManager::getDataSource($this->ScaffoldModel->useDbConfig);
$prefixes = Configure::read('Routing.prefixes');
$scaffoldPrefix = $this->scaffoldActions;
if (isset($db)) {
if (empty($this->scaffoldActions)) {
$this->scaffoldActions = array(
'index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete'
);
} elseif (!empty($prefixes) && in_array($scaffoldPrefix, $prefixes)) {
$this->scaffoldActions = array(
$scaffoldPrefix . '_index',
$scaffoldPrefix . '_list',
$scaffoldPrefix . '_view',
$scaffoldPrefix . '_add',
$scaffoldPrefix . '_create',
$scaffoldPrefix . '_edit',
$scaffoldPrefix . '_update',
$scaffoldPrefix . '_delete'
);
}
if (in_array($params['action'], $this->scaffoldActions)) {
if (!empty($prefixes)) {
$params['action'] = str_replace($scaffoldPrefix . '_', '', $params['action']);
}
switch ($params['action']) {
case 'index':
case 'list':
$this->__scaffoldIndex($params);
break;
case 'view':
$this->__scaffoldView($params);
break;
case 'add':
case 'create':
$this->__scaffoldSave($params, 'add');
break;
case 'edit':
case 'update':
$this->__scaffoldSave($params, 'edit');
break;
case 'delete':
$this->__scaffoldDelete($params);
break;
}
} else {
return $this->cakeError('missingAction', array(array(
'className' => $this->controller->name . "Controller",
'base' => $this->controller->base,
'action' => $this->action,
'webroot' => $this->controller->webroot
)));
}
} else {
return $this->cakeError('missingDatabase', array(array(
'webroot' => $this->controller->webroot
)));
}
}
/**
* Returns associations for controllers models.
*
* @return array Associations for model
* @access private
*/
function __associations() {
$keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
$associations = array();
foreach ($keys as $key => $type) {
foreach ($this->ScaffoldModel->{$type} as $assocKey => $assocData) {
$associations[$type][$assocKey]['primaryKey'] =
$this->ScaffoldModel->{$assocKey}->primaryKey;
$associations[$type][$assocKey]['displayField'] =
$this->ScaffoldModel->{$assocKey}->displayField;
$associations[$type][$assocKey]['foreignKey'] =
$assocData['foreignKey'];
$associations[$type][$assocKey]['controller'] =
Inflector::pluralize(Inflector::underscore($assocData['className']));
if ($type == 'hasAndBelongsToMany') {
$associations[$type][$assocKey]['with'] = $assocData['with'];
}
}
}
return $associations;
}
}
/**
* Scaffold View.
*
* @package cake
* @subpackage cake.cake.libs.controller
*/
if (!class_exists('ThemeView')) {
App::import('View', 'Theme');
}
/**
* ScaffoldView provides specific view file loading features for scaffolded views.
*
* @package cake.libs.view
*/
class ScaffoldView extends ThemeView {
/**
* Override _getViewFileName Appends special scaffolding views in.
*
* @param string $name name of the view file to get.
* @return string action
* @access protected
*/
function _getViewFileName($name = null) {
if ($name === null) {
$name = $this->action;
}
$name = Inflector::underscore($name);
$prefixes = Configure::read('Routing.prefixes');
if (!empty($prefixes)) {
foreach ($prefixes as $prefix) {
if (strpos($name, $prefix . '_') !== false) {
$name = substr($name, strlen($prefix) + 1);
break;
}
}
}
if ($name === 'add') {
$name = 'edit';
}
$scaffoldAction = 'scaffold.' . $name;
if (!is_null($this->subDir)) {
$subDir = strtolower($this->subDir) . DS;
} else {
$subDir = null;
}
$names[] = $this->viewPath . DS . $subDir . $scaffoldAction;
$names[] = 'scaffolds' . DS . $subDir . $name;
$paths = $this->_paths($this->plugin);
$exts = array($this->ext);
if ($this->ext !== '.ctp') {
array_push($exts, '.ctp');
}
foreach ($exts as $ext) {
foreach ($paths as $path) {
foreach ($names as $name) {
if (file_exists($path . $name . $ext)) {
return $path . $name . $ext;
}
}
}
}
if ($name === 'scaffolds' . DS . $subDir . 'error') {
return LIBS . 'view' . DS . 'errors' . DS . 'scaffold_error.ctp';
}
return $this->_missingView($paths[0] . $name . $this->ext, 'missingView');
}
}