348 lines
9.8 KiB
PHP
348 lines
9.8 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Classes and functions for the LDAP tree.
|
||
|
*
|
||
|
* @author The phpLDAPadmin development team
|
||
|
* @package phpLDAPadmin
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Abstract class which represents the LDAP tree view ; the draw() method
|
||
|
* must be implemented by subclasses
|
||
|
*
|
||
|
* @package phpLDAPadmin
|
||
|
* @subpackage Tree
|
||
|
* @see HTMLTree AJAXTree
|
||
|
*/
|
||
|
abstract class Tree {
|
||
|
# Server that this tree represents
|
||
|
private $server_id = null;
|
||
|
# List of entries in the tree view cache
|
||
|
protected $entries = array();
|
||
|
|
||
|
/**
|
||
|
* Displays the LDAP tree
|
||
|
*/
|
||
|
abstract public function draw();
|
||
|
|
||
|
protected function __construct($server_id) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$this->server_id = $server_id;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create an instance of the tree - this is used when we call this class directly
|
||
|
* Tree::getInstance($index)
|
||
|
*
|
||
|
* @return object Tree
|
||
|
*/
|
||
|
static public function getInstance($server_id) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$tree = get_cached_item($server_id,'tree');
|
||
|
|
||
|
if (! $tree) {
|
||
|
$server = $_SESSION[APPCONFIG]->getServer($server_id);
|
||
|
|
||
|
if (! $server)
|
||
|
return null;
|
||
|
|
||
|
$treeclass = $_SESSION[APPCONFIG]->getValue('appearance','tree');
|
||
|
$tree = new $treeclass($server_id);
|
||
|
|
||
|
# If we are not logged in, just return the empty tree.
|
||
|
if (is_null($server->getLogin(null)))
|
||
|
return $tree;
|
||
|
|
||
|
foreach ($server->getBaseDN(null) as $base)
|
||
|
if ($base) {
|
||
|
$tree->addEntry($base);
|
||
|
$baseEntry = $tree->getEntry($base);
|
||
|
$baseEntry->open();
|
||
|
}
|
||
|
|
||
|
set_cached_item($server_id,'tree','null',$tree);
|
||
|
}
|
||
|
|
||
|
return $tree;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the Server ID for this tree
|
||
|
*
|
||
|
* @return int Server ID that this tree is for
|
||
|
*/
|
||
|
protected function getServerID() {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->server_id);
|
||
|
|
||
|
return $this->server_id;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the server Object for this tree
|
||
|
*
|
||
|
* @return object Server Object for this tree
|
||
|
*/
|
||
|
protected function getServer() {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
return $_SESSION[APPCONFIG]->getServer($this->server_id);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the entries that are BaseDN entries.
|
||
|
*
|
||
|
* @return array Base DN entries
|
||
|
*/
|
||
|
public function getBaseEntries() {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$return = array();
|
||
|
|
||
|
foreach ($this->entries as $details)
|
||
|
if ($details->isBaseDN())
|
||
|
array_push($return,$details);
|
||
|
|
||
|
return $return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This function will take the DN, convert it to lowercase and strip unnessary
|
||
|
* commas. This result will be used as the index for the tree object.
|
||
|
* Any display of a DN should use the object->dn entry, not the index.
|
||
|
* The reason we need to do this is because:
|
||
|
* uid=User A,ou=People,c=AU and
|
||
|
* uid=User B, ou=PeOpLe, c=au
|
||
|
* are infact in the same branch, but PLA will show them inconsistently.
|
||
|
*
|
||
|
* @param dn DN to clean
|
||
|
* @return dn Lowercase clean DN
|
||
|
*/
|
||
|
private function indexDN($dn) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$index = strtolower(implode(',',pla_explode_dn($dn)));
|
||
|
|
||
|
if (DEBUG_ENABLED)
|
||
|
debug_log('Result (%s)',1,0,__FILE__,__LINE__,__METHOD__,$index);
|
||
|
|
||
|
return $index;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a tree entry
|
||
|
*
|
||
|
* @param dn DN to retrieve
|
||
|
* @return object Tree DN object
|
||
|
*/
|
||
|
public function getEntry($dn) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$dnlower = $this->indexDN($dn);
|
||
|
|
||
|
if (isset($this->entries[$dnlower]))
|
||
|
return $this->entries[$dnlower];
|
||
|
else
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add an entry in the tree view ; the entry is added in the
|
||
|
* children array of its parent
|
||
|
*
|
||
|
* @param dn DN to add
|
||
|
* @param string $dn the dn of the entry to create
|
||
|
*/
|
||
|
public function addEntry($dn) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$server = $this->getServer();
|
||
|
$dnlower = $this->indexDN($dn);
|
||
|
|
||
|
# @todo Temporarily removed, some non-ascii char DNs that do exist, fail here for some reason?
|
||
|
#if (! ($server->dnExists($dn)))
|
||
|
# return;
|
||
|
|
||
|
if (isset($this->entries[$dnlower]))
|
||
|
debug_dump_backtrace('Calling add entry to an entry that ALREADY exists?',1);
|
||
|
|
||
|
if (DEBUG_ENABLED)
|
||
|
debug_log('New ENTRY (%s).',64,0,__FILE__,__LINE__,__METHOD__,$dn);
|
||
|
|
||
|
$tree_factory = new TreeItem($server->getIndex(),$dn);
|
||
|
$tree_factory->setObjectClasses($server->getDNAttrValue($dn,'objectClass'));
|
||
|
|
||
|
if ((($isleaf = $server->getDNAttrValue($dn,'hassubordinates')) && ! strcasecmp($isleaf[0],'false')))
|
||
|
$tree_factory->setLeaf();
|
||
|
|
||
|
$this->entries[$dnlower] = $tree_factory;
|
||
|
|
||
|
# Is this entry in a base entry?
|
||
|
if (in_array_ignore_case($dn,$server->getBaseDN(null))) {
|
||
|
$this->entries[$dnlower]->setBase();
|
||
|
|
||
|
# If the parent entry is not in the tree, we add it. This routine will in itself
|
||
|
# recall this method until we get to the top of the tree (the base).
|
||
|
} else {
|
||
|
$parent_dn = $server->getContainer($dn);
|
||
|
|
||
|
if (DEBUG_ENABLED)
|
||
|
debug_log('Parent DNs (%s)',64,0,__FILE__,__LINE__,__METHOD__,$parent_dn);
|
||
|
|
||
|
if ($parent_dn) {
|
||
|
$parent_entry = $this->getEntry($parent_dn);
|
||
|
|
||
|
if (! $parent_entry) {
|
||
|
$this->addEntry($parent_dn);
|
||
|
$parent_entry = $this->getEntry($parent_dn);
|
||
|
}
|
||
|
|
||
|
# Update this DN's parent's children list as well.
|
||
|
$parent_entry->addChild($dn);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete an entry from the tree view ; the entry is deleted from the
|
||
|
* children array of its parent
|
||
|
*
|
||
|
* @param dn DN to remote
|
||
|
*/
|
||
|
public function delEntry($dn) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$server = $this->getServer();
|
||
|
$dnlower = $this->indexDN($dn);
|
||
|
|
||
|
if (isset($this->entries[$dnlower]))
|
||
|
unset($this->entries[$dnlower]);
|
||
|
|
||
|
# Delete entry from parent's children as well.
|
||
|
$parent_dn = $server->getContainer($dn);
|
||
|
$parent_entry = $this->getEntry($parent_dn);
|
||
|
|
||
|
if ($parent_entry)
|
||
|
$parent_entry->delChild($dn);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Rename an entry in the tree
|
||
|
*
|
||
|
* @param dn Old DN
|
||
|
* @param dn New DN
|
||
|
*/
|
||
|
public function renameEntry($dnOLD,$dnNEW) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$server = $this->getServer();
|
||
|
$dnlowerOLD = $this->indexDN($dnOLD);
|
||
|
$dnlowerNEW = $this->indexDN($dnNEW);
|
||
|
|
||
|
$this->entries[$dnlowerNEW] = $this->entries[$dnlowerOLD];
|
||
|
if ($dnlowerOLD != $dnlowerNEW)
|
||
|
unset($this->entries[$dnlowerOLD]);
|
||
|
$this->entries[$dnlowerNEW]->rename($dnNEW);
|
||
|
|
||
|
# Update the parent's children
|
||
|
$parentNEW = $server->getContainer($dnNEW);
|
||
|
$parentOLD = $server->getContainer($dnOLD);
|
||
|
|
||
|
$parent_entry = $this->getEntry($parentNEW);
|
||
|
if ($parent_entry)
|
||
|
$parent_entry->addChild($dnNEW);
|
||
|
|
||
|
$parent_entry = $this->getEntry($parentOLD);
|
||
|
if ($parent_entry)
|
||
|
$parent_entry->delChild($dnOLD);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read the children of a tree entry
|
||
|
*
|
||
|
* @param dn DN of the entry
|
||
|
* @param boolean LDAP Size Limit
|
||
|
*/
|
||
|
public function readChildren($dn,$nolimit=false) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$server = $this->getServer();
|
||
|
$dnlower = $this->indexDN($dn);
|
||
|
|
||
|
if (! isset($this->entries[$dnlower]))
|
||
|
debug_dump_backtrace('Reading children on an entry that isnt set? '.$dnlower,true);
|
||
|
|
||
|
$ldap['child_limit'] = $nolimit ? 0 : $_SESSION[APPCONFIG]->getValue('search','size_limit');
|
||
|
$ldap['filter'] = $_SESSION[APPCONFIG]->getValue('appearance','tree_filter');
|
||
|
$ldap['deref'] = $_SESSION[APPCONFIG]->getValue('deref','tree');
|
||
|
|
||
|
# Perform the query to get the children.
|
||
|
$ldap['children'] = $server->getContainerContents($dn,null,$ldap['child_limit'],$ldap['filter'],$ldap['deref']);
|
||
|
|
||
|
if (! count($ldap['children'])) {
|
||
|
$this->entries[$dnlower]->unsetSizeLimited();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (DEBUG_ENABLED)
|
||
|
debug_log('Children of (%s) are (%s)',64,0,__FILE__,__LINE__,__METHOD__,$dn,$ldap['children']);
|
||
|
|
||
|
# Relax our execution time, it might take some time to load this
|
||
|
if ($nolimit)
|
||
|
@set_time_limit($_SESSION[APPCONFIG]->getValue('search','time_limit'));
|
||
|
|
||
|
$this->entries[$dnlower]->readingChildren(true);
|
||
|
|
||
|
foreach ($ldap['children'] as $child) {
|
||
|
if (DEBUG_ENABLED)
|
||
|
debug_log('Adding (%s)',64,0,__FILE__,__LINE__,__METHOD__,$child);
|
||
|
|
||
|
if (! in_array($child,$this->entries[$dnlower]->getChildren()))
|
||
|
$this->entries[$dnlower]->addChild($child);
|
||
|
}
|
||
|
|
||
|
$this->entries[$dnlower]->readingChildren(false);
|
||
|
|
||
|
if (count($this->entries[$dnlower]->getChildren()) == $ldap['child_limit'])
|
||
|
$this->entries[$dnlower]->setSizeLimited();
|
||
|
else
|
||
|
$this->entries[$dnlower]->unsetSizeLimited();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the number of children an entry has. Optionally autoread the child entry.
|
||
|
*
|
||
|
* @param dn DN of the entry
|
||
|
* @param boolean LDAP Size Limit
|
||
|
*/
|
||
|
protected function readChildrenNumber($dn,$nolimit=false) {
|
||
|
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
|
||
|
debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs);
|
||
|
|
||
|
$dnlower = $this->indexDN($dn);
|
||
|
|
||
|
if (! isset($this->entries[$dnlower]))
|
||
|
debug_dump_backtrace('Reading children on an entry that isnt set?',true);
|
||
|
|
||
|
# Read the entry if we havent got it yet.
|
||
|
if (! $this->entries[$dnlower]->isLeaf() && ! $this->entries[$dnlower]->getChildren())
|
||
|
$this->readChildren($dn,$nolimit);
|
||
|
|
||
|
return count($this->entries[$dnlower]->getChildren());
|
||
|
}
|
||
|
}
|
||
|
?>
|