File indexing completed on 2024-05-12 06:02:43

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Ldap
0017  * @subpackage Node
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Ldap
0025  */
0026 // require_once 'Zend/Ldap.php';
0027 /**
0028  * @see Zend_Ldap_Node_Abstract
0029  */
0030 // require_once 'Zend/Ldap/Node/Abstract.php';
0031 
0032 /**
0033  * Zend_Ldap_Node provides an object oriented view into a LDAP node.
0034  *
0035  * @category   Zend
0036  * @package    Zend_Ldap
0037  * @subpackage Node
0038  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0039  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0040  */
0041 class Zend_Ldap_Node extends Zend_Ldap_Node_Abstract implements Iterator, RecursiveIterator
0042 {
0043     /**
0044      * Holds the node's new DN if node is renamed.
0045      *
0046      * @var Zend_Ldap_Dn
0047      */
0048     protected $_newDn;
0049     /**
0050      * Holds the node's orginal attributes (as loaded).
0051      *
0052      * @var array
0053      */
0054     protected $_originalData;
0055     /**
0056      * This node will be added
0057      *
0058      * @var boolean
0059      */
0060     protected $_new;
0061     /**
0062      * This node will be deleted
0063      *
0064      * @var boolean
0065      */
0066     protected $_delete;
0067     /**
0068      * Holds the connection to the LDAP server if in connected mode.
0069      *
0070      * @var Zend_Ldap
0071      */
0072     protected $_ldap;
0073 
0074     /**
0075      * Holds an array of the current node's children.
0076      *
0077      * @var array
0078      */
0079     protected $_children;
0080 
0081     /**
0082      * Controls iteration status
0083      *
0084      * @var boolean
0085      */
0086     private $_iteratorRewind = false;
0087 
0088     /**
0089      * Constructor.
0090      *
0091      * Constructor is protected to enforce the use of factory methods.
0092      *
0093      * @param  Zend_Ldap_Dn $dn
0094      * @param  array        $data
0095      * @param  boolean      $fromDataSource
0096      * @param  Zend_Ldap    $ldap
0097      * @throws Zend_Ldap_Exception
0098      */
0099     protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource, Zend_Ldap $ldap = null)
0100     {
0101         parent::__construct($dn, $data, $fromDataSource);
0102         if ($ldap !== null) $this->attachLdap($ldap);
0103         else $this->detachLdap();
0104     }
0105 
0106     /**
0107      * Serialization callback
0108      *
0109      * Only DN and attributes will be serialized.
0110      *
0111      * @return array
0112      */
0113     public function __sleep()
0114     {
0115         return array('_dn', '_currentData', '_newDn', '_originalData',
0116             '_new', '_delete', '_children');
0117     }
0118 
0119     /**
0120      * Deserialization callback
0121      *
0122      * Enforces a detached node.
0123      *
0124      * @return null
0125      */
0126     public function __wakeup()
0127     {
0128         $this->detachLdap();
0129     }
0130 
0131     /**
0132      * Gets the current LDAP connection.
0133      *
0134      * @return Zend_Ldap
0135      * @throws Zend_Ldap_Exception
0136      */
0137     public function getLdap()
0138     {
0139         if ($this->_ldap === null) {
0140             /**
0141              * @see Zend_Ldap_Exception
0142              */
0143             // require_once 'Zend/Ldap/Exception.php';
0144             throw new Zend_Ldap_Exception(null, 'No LDAP connection specified.', Zend_Ldap_Exception::LDAP_OTHER);
0145         }
0146         else return $this->_ldap;
0147     }
0148 
0149     /**
0150      * Attach node to an LDAP connection
0151      *
0152      * This is an offline method.
0153      *
0154      * @uses   Zend_Ldap_Dn::isChildOf()
0155      * @param  Zend_Ldap $ldap
0156      * @return Zend_Ldap_Node Provides a fluent interface
0157      * @throws Zend_Ldap_Exception
0158      */
0159     public function attachLdap(Zend_Ldap $ldap)
0160     {
0161         if (!Zend_Ldap_Dn::isChildOf($this->_getDn(), $ldap->getBaseDn())) {
0162             /**
0163              * @see Zend_Ldap_Exception
0164              */
0165             // require_once 'Zend/Ldap/Exception.php';
0166             throw new Zend_Ldap_Exception(null, 'LDAP connection is not responsible for given node.',
0167                 Zend_Ldap_Exception::LDAP_OTHER);
0168         }
0169 
0170         if ($ldap !== $this->_ldap) {
0171             $this->_ldap = $ldap;
0172             if (is_array($this->_children)) {
0173                 foreach ($this->_children as $child) {
0174                     /* @var Zend_Ldap_Node $child */
0175                     $child->attachLdap($ldap);
0176                 }
0177             }
0178         }
0179         return $this;
0180     }
0181 
0182     /**
0183      * Detach node from LDAP connection
0184      *
0185      * This is an offline method.
0186      *
0187      * @return Zend_Ldap_Node Provides a fluent interface
0188      */
0189     public function detachLdap()
0190     {
0191         $this->_ldap = null;
0192         if (is_array($this->_children)) {
0193             foreach ($this->_children as $child) {
0194                 /* @var Zend_Ldap_Node $child */
0195                 $child->detachLdap();
0196             }
0197         }
0198         return $this;
0199     }
0200 
0201     /**
0202      * Checks if the current node is attached to a LDAP server.
0203      *
0204      * This is an offline method.
0205      *
0206      * @return boolean
0207      */
0208     public function isAttached()
0209     {
0210         return ($this->_ldap !== null);
0211     }
0212 
0213     /**
0214      * @param  array   $data
0215      * @param  boolean $fromDataSource
0216      * @throws Zend_Ldap_Exception
0217      */
0218     protected function _loadData(array $data, $fromDataSource)
0219     {
0220         parent::_loadData($data, $fromDataSource);
0221         if ($fromDataSource === true) {
0222             $this->_originalData = $data;
0223         } else {
0224             $this->_originalData = array();
0225         }
0226         $this->_children = null;
0227         $this->_markAsNew(($fromDataSource === true) ? false : true);
0228         $this->_markAsToBeDeleted(false);
0229     }
0230 
0231     /**
0232      * Factory method to create a new detached Zend_Ldap_Node for a given DN.
0233      *
0234      * @param  string|array|Zend_Ldap_Dn $dn
0235      * @param  array                     $objectClass
0236      * @return Zend_Ldap_Node
0237      * @throws Zend_Ldap_Exception
0238      */
0239     public static function create($dn, array $objectClass = array())
0240     {
0241         if (is_string($dn) || is_array($dn)) {
0242             $dn = Zend_Ldap_Dn::factory($dn);
0243         } else if ($dn instanceof Zend_Ldap_Dn) {
0244             $dn = clone $dn;
0245         } else {
0246             /**
0247              * @see Zend_Ldap_Exception
0248              */
0249             // require_once 'Zend/Ldap/Exception.php';
0250             throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
0251         }
0252         $new = new self($dn, array(), false, null);
0253         $new->_ensureRdnAttributeValues();
0254         $new->setAttribute('objectClass', $objectClass);
0255         return $new;
0256     }
0257 
0258     /**
0259      * Factory method to create an attached Zend_Ldap_Node for a given DN.
0260      *
0261      * @param  string|array|Zend_Ldap_Dn $dn
0262      * @param  Zend_Ldap                 $ldap
0263      * @return Zend_Ldap_Node|null
0264      * @throws Zend_Ldap_Exception
0265      */
0266     public static function fromLdap($dn, Zend_Ldap $ldap)
0267     {
0268         if (is_string($dn) || is_array($dn)) {
0269             $dn = Zend_Ldap_Dn::factory($dn);
0270         } else if ($dn instanceof Zend_Ldap_Dn) {
0271             $dn = clone $dn;
0272         } else {
0273             /**
0274              * @see Zend_Ldap_Exception
0275              */
0276             // require_once 'Zend/Ldap/Exception.php';
0277             throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
0278         }
0279         $data = $ldap->getEntry($dn, array('*', '+'), true);
0280         if ($data === null) {
0281             return null;
0282         }
0283         $entry = new self($dn, $data, true, $ldap);
0284         return $entry;
0285     }
0286 
0287     /**
0288      * Factory method to create a detached Zend_Ldap_Node from array data.
0289      *
0290      * @param  array   $data
0291      * @param  boolean $fromDataSource
0292      * @return Zend_Ldap_Node
0293      * @throws Zend_Ldap_Exception
0294      */
0295     public static function fromArray(array $data, $fromDataSource = false)
0296     {
0297         if (!array_key_exists('dn', $data)) {
0298             /**
0299              * @see Zend_Ldap_Exception
0300              */
0301             // require_once 'Zend/Ldap/Exception.php';
0302             throw new Zend_Ldap_Exception(null, '\'dn\' key is missing in array.');
0303         }
0304         if (is_string($data['dn']) || is_array($data['dn'])) {
0305             $dn = Zend_Ldap_Dn::factory($data['dn']);
0306         } else if ($data['dn'] instanceof Zend_Ldap_Dn) {
0307             $dn = clone $data['dn'];
0308         } else {
0309             /**
0310              * @see Zend_Ldap_Exception
0311              */
0312             // require_once 'Zend/Ldap/Exception.php';
0313             throw new Zend_Ldap_Exception(null, '\'dn\' key is of a wrong data type.');
0314         }
0315         $fromDataSource = ($fromDataSource === true) ? true : false;
0316         $new = new self($dn, $data, $fromDataSource, null);
0317         $new->_ensureRdnAttributeValues();
0318         return $new;
0319     }
0320 
0321     /**
0322      * Ensures that teh RDN attributes are correctly set.
0323      *
0324      * @param  boolean    $overwrite    True to overwrite the RDN attributes
0325      * @return void
0326      */
0327     protected function _ensureRdnAttributeValues($overwrite = false)
0328     {
0329         foreach ($this->getRdnArray() as $key => $value) {
0330             if (!array_key_exists($key, $this->_currentData) || $overwrite) {
0331                 Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, false);
0332             } else if (!in_array($value, $this->_currentData[$key])) {
0333                 Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, true);
0334             }
0335         }
0336     }
0337 
0338     /**
0339      * Marks this node as new.
0340      *
0341      * Node will be added (instead of updated) on calling update() if $new is true.
0342      *
0343      * @param boolean $new
0344      */
0345     protected function _markAsNew($new)
0346     {
0347         $this->_new = ($new === false) ? false : true;
0348     }
0349 
0350     /**
0351      * Tells if the node is consiedered as new (not present on the server)
0352      *
0353      * Please note, that this doesn't tell you if the node is present on the server.
0354      * Use {@link exits()} to see if a node is already there.
0355      *
0356      * @return boolean
0357      */
0358     public function isNew()
0359     {
0360         return $this->_new;
0361     }
0362 
0363     /**
0364      * Marks this node as to be deleted.
0365      *
0366      * Node will be deleted on calling update() if $delete is true.
0367      *
0368      * @param boolean $delete
0369      */
0370     protected function _markAsToBeDeleted($delete)
0371     {
0372         $this->_delete = ($delete === true) ? true : false;
0373     }
0374 
0375 
0376     /**
0377     * Is this node going to be deleted once update() is called?
0378     *
0379     * @return boolean
0380     */
0381     public function willBeDeleted()
0382     {
0383         return $this->_delete;
0384     }
0385 
0386     /**
0387      * Marks this node as to be deleted
0388      *
0389      * Node will be deleted on calling update() if $delete is true.
0390      *
0391      * @return Zend_Ldap_Node Provides a fluent interface
0392      */
0393     public function delete()
0394     {
0395         $this->_markAsToBeDeleted(true);
0396         return $this;
0397     }
0398 
0399     /**
0400     * Is this node going to be moved once update() is called?
0401     *
0402     * @return boolean
0403     */
0404     public function willBeMoved()
0405     {
0406         if ($this->isNew() || $this->willBeDeleted()) {
0407             return false;
0408         } else if ($this->_newDn !== null) {
0409             return ($this->_dn != $this->_newDn);
0410         } else {
0411             return false;
0412         }
0413     }
0414 
0415     /**
0416      * Sends all pending changes to the LDAP server
0417      *
0418      * @param  Zend_Ldap $ldap
0419      * @return Zend_Ldap_Node Provides a fluent interface
0420      * @throws Zend_Ldap_Exception
0421      */
0422     public function update(Zend_Ldap $ldap = null)
0423     {
0424         if ($ldap !== null) {
0425             $this->attachLdap($ldap);
0426         }
0427         $ldap = $this->getLdap();
0428         if (!($ldap instanceof Zend_Ldap)) {
0429             /**
0430              * @see Zend_Ldap_Exception
0431              */
0432             // require_once 'Zend/Ldap/Exception.php';
0433             throw new Zend_Ldap_Exception(null, 'No LDAP connection available');
0434         }
0435 
0436         if ($this->willBeDeleted()) {
0437             if ($ldap->exists($this->_dn)) {
0438                 $this->_preDelete();
0439                 $ldap->delete($this->_dn);
0440                 $this->_postDelete();
0441             }
0442             return $this;
0443         }
0444 
0445         if ($this->isNew()) {
0446             $this->_preAdd();
0447             $data = $this->getData();
0448             $ldap->add($this->_getDn(), $data);
0449             $this->_loadData($data, true);
0450             $this->_postAdd();
0451             return $this;
0452         }
0453 
0454         $changedData = $this->getChangedData();
0455         if ($this->willBeMoved()) {
0456             $this->_preRename();
0457             $recursive = $this->hasChildren();
0458             $ldap->rename($this->_dn, $this->_newDn, $recursive, false);
0459             foreach ($this->_newDn->getRdn() as $key => $value) {
0460                 if (array_key_exists($key, $changedData)) {
0461                     unset($changedData[$key]);
0462                 }
0463             }
0464             $this->_dn = $this->_newDn;
0465             $this->_newDn = null;
0466             $this->_postRename();
0467         }
0468         if (count($changedData) > 0) {
0469             $this->_preUpdate();
0470             $ldap->update($this->_getDn(), $changedData);
0471             $this->_postUpdate();
0472         }
0473         $this->_originalData = $this->_currentData;
0474         return $this;
0475     }
0476 
0477     /**
0478      * Gets the DN of the current node as a Zend_Ldap_Dn.
0479      *
0480      * This is an offline method.
0481      *
0482      * @return Zend_Ldap_Dn
0483      */
0484     protected function _getDn()
0485     {
0486         return ($this->_newDn === null) ? parent::_getDn() : $this->_newDn;
0487     }
0488 
0489     /**
0490      * Gets the current DN of the current node as a Zend_Ldap_Dn.
0491      * The method returns a clone of the node's DN to prohibit modification.
0492      *
0493      * This is an offline method.
0494      *
0495      * @return Zend_Ldap_Dn
0496      */
0497     public function getCurrentDn()
0498     {
0499         $dn = clone parent::_getDn();
0500         return $dn;
0501     }
0502 
0503     /**
0504      * Sets the new DN for this node
0505      *
0506      * This is an offline method.
0507      *
0508      * @param  Zend_Ldap_Dn|string|array $newDn
0509      * @throws Zend_Ldap_Exception
0510      * @return Zend_Ldap_Node Provides a fluent interface
0511      */
0512     public function setDn($newDn)
0513     {
0514         if ($newDn instanceof Zend_Ldap_Dn) {
0515             $this->_newDn = clone $newDn;
0516         } else {
0517             $this->_newDn = Zend_Ldap_Dn::factory($newDn);
0518         }
0519         $this->_ensureRdnAttributeValues(true);
0520         return $this;
0521     }
0522 
0523     /**
0524      * {@see setDn()}
0525      *
0526      * This is an offline method.
0527      *
0528      * @param  Zend_Ldap_Dn|string|array $newDn
0529      * @throws Zend_Ldap_Exception
0530      * @return Zend_Ldap_Node Provides a fluent interface
0531      */
0532     public function move($newDn)
0533     {
0534         return $this->setDn($newDn);
0535     }
0536 
0537     /**
0538      * {@see setDn()}
0539      *
0540      * This is an offline method.
0541      *
0542      * @param  Zend_Ldap_Dn|string|array $newDn
0543      * @throws Zend_Ldap_Exception
0544      * @return Zend_Ldap_Node Provides a fluent interface
0545      */
0546     public function rename($newDn)
0547     {
0548         return $this->setDn($newDn);
0549     }
0550 
0551     /**
0552      * Sets the objectClass.
0553      *
0554      * This is an offline method.
0555      *
0556      * @param  array|string $value
0557      * @return Zend_Ldap_Node Provides a fluent interface
0558      * @throws Zend_Ldap_Exception
0559      */
0560     public function setObjectClass($value)
0561     {
0562         $this->setAttribute('objectClass', $value);
0563         return $this;
0564     }
0565 
0566     /**
0567      * Appends to the objectClass.
0568      *
0569      * This is an offline method.
0570      *
0571      * @param  array|string $value
0572      * @return Zend_Ldap_Node Provides a fluent interface
0573      * @throws Zend_Ldap_Exception
0574      */
0575     public function appendObjectClass($value)
0576     {
0577         $this->appendToAttribute('objectClass', $value);
0578         return $this;
0579     }
0580 
0581     /**
0582      * Returns a LDIF representation of the current node
0583      *
0584      * @param  array $options Additional options used during encoding
0585      * @return string
0586      */
0587     public function toLdif(array $options = array())
0588     {
0589         $attributes = array_merge(array('dn' => $this->getDnString()), $this->getData(false));
0590         /**
0591          * Zend_Ldap_Ldif_Encoder
0592          */
0593         // require_once 'Zend/Ldap/Ldif/Encoder.php';
0594         return Zend_Ldap_Ldif_Encoder::encode($attributes, $options);
0595     }
0596 
0597     /**
0598      * Gets changed node data.
0599      *
0600      * The array contains all changed attributes.
0601      * This format can be used in {@link Zend_Ldap::add()} and {@link Zend_Ldap::update()}.
0602      *
0603      * This is an offline method.
0604      *
0605      * @return array
0606      */
0607     public function getChangedData()
0608     {
0609         $changed = array();
0610         foreach ($this->_currentData as $key => $value) {
0611             if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
0612                 $changed[$key] = $value;
0613             } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
0614                 $changed[$key] = $value;
0615             }
0616         }
0617         return $changed;
0618     }
0619 
0620     /**
0621      * Returns all changes made.
0622      *
0623      * This is an offline method.
0624      *
0625      * @return array
0626      */
0627     public function getChanges()
0628     {
0629         $changes = array(
0630             'add'     => array(),
0631             'delete'  => array(),
0632             'replace' => array());
0633         foreach ($this->_currentData as $key => $value) {
0634             if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
0635                 $changes['add'][$key] = $value;
0636             } else if (count($this->_originalData[$key]) === 0 && !empty($value)) {
0637                 $changes['add'][$key] = $value;
0638             } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
0639                 if (empty($value)) {
0640                     $changes['delete'][$key] = $value;
0641                 } else {
0642                     $changes['replace'][$key] = $value;
0643                 }
0644             }
0645         }
0646         return $changes;
0647     }
0648 
0649     /**
0650      * Sets a LDAP attribute.
0651      *
0652      * This is an offline method.
0653      *
0654      * @param  string $name
0655      * @param  mixed  $value
0656      * @return Zend_Ldap_Node Provides a fluent interface
0657      * @throws Zend_Ldap_Exception
0658      */
0659     public function setAttribute($name, $value)
0660     {
0661         $this->_setAttribute($name, $value, false);
0662         return $this;
0663     }
0664 
0665     /**
0666      * Appends to a LDAP attribute.
0667      *
0668      * This is an offline method.
0669      *
0670      * @param  string $name
0671      * @param  mixed  $value
0672      * @return Zend_Ldap_Node Provides a fluent interface
0673      * @throws Zend_Ldap_Exception
0674      */
0675     public function appendToAttribute($name, $value)
0676     {
0677         $this->_setAttribute($name, $value, true);
0678         return $this;
0679     }
0680 
0681     /**
0682      * Checks if the attribute can be set and sets it accordingly.
0683      *
0684      * @param  string  $name
0685      * @param  mixed   $value
0686      * @param  boolean $append
0687      * @throws Zend_Ldap_Exception
0688      */
0689     protected function _setAttribute($name, $value, $append)
0690     {
0691         $this->_assertChangeableAttribute($name);
0692         Zend_Ldap_Attribute::setAttribute($this->_currentData, $name, $value, $append);
0693     }
0694 
0695     /**
0696      * Sets a LDAP date/time attribute.
0697      *
0698      * This is an offline method.
0699      *
0700      * @param  string        $name
0701      * @param  integer|array $value
0702      * @param  boolean       $utc
0703      * @return Zend_Ldap_Node Provides a fluent interface
0704      * @throws Zend_Ldap_Exception
0705      */
0706     public function setDateTimeAttribute($name, $value, $utc = false)
0707     {
0708         $this->_setDateTimeAttribute($name, $value, $utc, false);
0709         return $this;
0710     }
0711 
0712     /**
0713      * Appends to a LDAP date/time attribute.
0714      *
0715      * This is an offline method.
0716      *
0717      * @param  string        $name
0718      * @param  integer|array $value
0719      * @param  boolean       $utc
0720      * @return Zend_Ldap_Node Provides a fluent interface
0721      * @throws Zend_Ldap_Exception
0722      */
0723     public function appendToDateTimeAttribute($name, $value, $utc = false)
0724     {
0725         $this->_setDateTimeAttribute($name, $value, $utc, true);
0726         return $this;
0727     }
0728 
0729     /**
0730      * Checks if the attribute can be set and sets it accordingly.
0731      *
0732      * @param  string        $name
0733      * @param  integer|array $value
0734      * @param  boolean       $utc
0735      * @param  boolean       $append
0736      * @throws Zend_Ldap_Exception
0737      */
0738     protected function _setDateTimeAttribute($name, $value, $utc, $append)
0739     {
0740         $this->_assertChangeableAttribute($name);
0741         Zend_Ldap_Attribute::setDateTimeAttribute($this->_currentData, $name, $value, $utc, $append);
0742     }
0743 
0744     /**
0745      * Sets a LDAP password.
0746      *
0747      * @param  string $password
0748      * @param  string $hashType
0749      * @param  string $attribName
0750      * @return Zend_Ldap_Node Provides a fluent interface
0751      * @throws Zend_Ldap_Exception
0752      */
0753     public function setPasswordAttribute($password, $hashType = Zend_Ldap_Attribute::PASSWORD_HASH_MD5,
0754         $attribName = 'userPassword')
0755     {
0756         $this->_assertChangeableAttribute($attribName);
0757         Zend_Ldap_Attribute::setPassword($this->_currentData, $password, $hashType, $attribName);
0758         return $this;
0759     }
0760 
0761     /**
0762      * Deletes a LDAP attribute.
0763      *
0764      * This method deletes the attribute.
0765      *
0766      * This is an offline method.
0767      *
0768      * @param  string $name
0769      * @return Zend_Ldap_Node Provides a fluent interface
0770      * @throws Zend_Ldap_Exception
0771      */
0772     public function deleteAttribute($name)
0773     {
0774         if ($this->existsAttribute($name, true)) {
0775             $this->_setAttribute($name, null, false);
0776         }
0777         return $this;
0778     }
0779 
0780     /**
0781      * Removes duplicate values from a LDAP attribute
0782      *
0783      * @param  string $attribName
0784      * @return void
0785      */
0786     public function removeDuplicatesFromAttribute($attribName)
0787     {
0788         Zend_Ldap_Attribute::removeDuplicatesFromAttribute($this->_currentData, $attribName);
0789     }
0790 
0791     /**
0792      * Remove given values from a LDAP attribute
0793      *
0794      * @param  string      $attribName
0795      * @param  mixed|array $value
0796      * @return void
0797      */
0798     public function removeFromAttribute($attribName, $value)
0799     {
0800         Zend_Ldap_Attribute::removeFromAttribute($this->_currentData, $attribName, $value);
0801     }
0802 
0803     /**
0804      * @param  string $name
0805      * @return boolean
0806      * @throws Zend_Ldap_Exception
0807      */
0808     protected function _assertChangeableAttribute($name)
0809     {
0810         $name = strtolower($name);
0811         $rdn = $this->getRdnArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
0812         if ($name == 'dn') {
0813             /**
0814              * @see Zend_Ldap_Exception
0815              */
0816             // require_once 'Zend/Ldap/Exception.php';
0817             throw new Zend_Ldap_Exception(null, 'DN cannot be changed.');
0818         }
0819         else if (array_key_exists($name, $rdn)) {
0820             /**
0821              * @see Zend_Ldap_Exception
0822              */
0823             // require_once 'Zend/Ldap/Exception.php';
0824             throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s part of the RDN');
0825         } else if (in_array($name, self::$_systemAttributes)) {
0826             /**
0827              * @see Zend_Ldap_Exception
0828              */
0829             // require_once 'Zend/Ldap/Exception.php';
0830             throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s read-only');
0831         }
0832         else return true;
0833     }
0834 
0835     /**
0836      * Sets a LDAP attribute.
0837      *
0838      * This is an offline method.
0839      *
0840      * @param  string $name
0841      * @param  mixed  $value
0842      * @return null
0843      * @throws Zend_Ldap_Exception
0844      */
0845     public function __set($name, $value)
0846     {
0847         $this->setAttribute($name, $value);
0848     }
0849 
0850     /**
0851      * Deletes a LDAP attribute.
0852      *
0853      * This method deletes the attribute.
0854      *
0855      * This is an offline method.
0856      *
0857      * @param  string $name
0858      * @return null
0859      * @throws Zend_Ldap_Exception
0860      */
0861     public function __unset($name)
0862     {
0863         $this->deleteAttribute($name);
0864     }
0865 
0866     /**
0867      * Sets a LDAP attribute.
0868      * Implements ArrayAccess.
0869      *
0870      * This is an offline method.
0871      *
0872      * @param  string $name
0873      * @param  mixed  $value
0874      * @return null
0875      * @throws Zend_Ldap_Exception
0876      */
0877     public function offsetSet($name, $value)
0878     {
0879         $this->setAttribute($name, $value);
0880     }
0881 
0882     /**
0883      * Deletes a LDAP attribute.
0884      * Implements ArrayAccess.
0885      *
0886      * This method deletes the attribute.
0887      *
0888      * This is an offline method.
0889      *
0890      * @param  string $name
0891      * @return null
0892      * @throws Zend_Ldap_Exception
0893      */
0894     public function offsetUnset($name)
0895     {
0896         $this->deleteAttribute($name);
0897     }
0898 
0899     /**
0900      * Check if node exists on LDAP.
0901      *
0902      * This is an online method.
0903      *
0904      * @param  Zend_Ldap $ldap
0905      * @return boolean
0906      * @throws Zend_Ldap_Exception
0907      */
0908     public function exists(Zend_Ldap $ldap = null)
0909     {
0910         if ($ldap !== null) {
0911             $this->attachLdap($ldap);
0912         }
0913         $ldap = $this->getLdap();
0914         return $ldap->exists($this->_getDn());
0915     }
0916 
0917     /**
0918      * Reload node attributes from LDAP.
0919      *
0920      * This is an online method.
0921      *
0922      * @param  Zend_Ldap $ldap
0923      * @return Zend_Ldap_Node Provides a fluent interface
0924      * @throws Zend_Ldap_Exception
0925      */
0926     public function reload(Zend_Ldap $ldap = null)
0927     {
0928         if ($ldap !== null) {
0929             $this->attachLdap($ldap);
0930         }
0931         $ldap = $this->getLdap();
0932         parent::reload($ldap);
0933         return $this;
0934     }
0935 
0936     /**
0937      * Search current subtree with given options.
0938      *
0939      * This is an online method.
0940      *
0941      * @param  string|Zend_Ldap_Filter_Abstract $filter
0942      * @param  integer                          $scope
0943      * @param  string                           $sort
0944      * @return Zend_Ldap_Node_Collection
0945      * @throws Zend_Ldap_Exception
0946      */
0947     public function searchSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB, $sort = null)
0948     {
0949         /**
0950          * @see Zend_Ldap_Node_Collection
0951          */
0952         // require_once 'Zend/Ldap/Node/Collection.php';
0953         return $this->getLdap()->search($filter, $this->_getDn(), $scope, array('*', '+'), $sort,
0954             'Zend_Ldap_Node_Collection');
0955     }
0956 
0957     /**
0958      * Count items in current subtree found by given filter.
0959      *
0960      * This is an online method.
0961      *
0962      * @param  string|Zend_Ldap_Filter_Abstract $filter
0963      * @param  integer                          $scope
0964      * @return integer
0965      * @throws Zend_Ldap_Exception
0966      */
0967     public function countSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB)
0968     {
0969         return $this->getLdap()->count($filter, $this->_getDn(), $scope);
0970     }
0971 
0972     /**
0973      * Count children of current node.
0974      *
0975      * This is an online method.
0976      *
0977      * @return integer
0978      * @throws Zend_Ldap_Exception
0979      */
0980     public function countChildren()
0981     {
0982         return $this->countSubtree('(objectClass=*)', Zend_Ldap::SEARCH_SCOPE_ONE);
0983     }
0984 
0985     /**
0986      * Gets children of current node.
0987      *
0988      * This is an online method.
0989      *
0990      * @param  string|Zend_Ldap_Filter_Abstract $filter
0991      * @param  string                           $sort
0992      * @return Zend_Ldap_Node_Collection
0993      * @throws Zend_Ldap_Exception
0994      */
0995     public function searchChildren($filter, $sort = null)
0996     {
0997         return $this->searchSubtree($filter, Zend_Ldap::SEARCH_SCOPE_ONE, $sort);
0998     }
0999 
1000     /**
1001      * Checks if current node has children.
1002      * Returns whether the current element has children.
1003      *
1004      * Can be used offline but returns false if children have not been retrieved yet.
1005      *
1006      * @return boolean
1007      * @throws Zend_Ldap_Exception
1008      */
1009     public function hasChildren()
1010     {
1011         if (!is_array($this->_children)) {
1012             if ($this->isAttached()) {
1013                 return ($this->countChildren() > 0);
1014             } else {
1015                 return false;
1016             }
1017         } else {
1018             return (count($this->_children) > 0);
1019         }
1020     }
1021 
1022     /**
1023      * Returns the children for the current node.
1024      *
1025      * Can be used offline but returns an empty array if children have not been retrieved yet.
1026      *
1027      * @return Zend_Ldap_Node_ChildrenIterator
1028      * @throws Zend_Ldap_Exception
1029      */
1030     public function getChildren()
1031     {
1032         if (!is_array($this->_children)) {
1033             $this->_children = array();
1034             if ($this->isAttached()) {
1035                 $children = $this->searchChildren('(objectClass=*)', null);
1036                 foreach ($children as $child) {
1037                     /* @var Zend_Ldap_Node $child */
1038                     $this->_children[$child->getRdnString(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER)] = $child;
1039                 }
1040             }
1041         }
1042         /**
1043          * @see Zend_Ldap_Node_ChildrenIterator
1044          */
1045         // require_once 'Zend/Ldap/Node/ChildrenIterator.php';
1046         return new Zend_Ldap_Node_ChildrenIterator($this->_children);
1047     }
1048 
1049     /**
1050      * Returns the parent of the current node.
1051      *
1052      * @param  Zend_Ldap $ldap
1053      * @return Zend_Ldap_Node
1054      * @throws Zend_Ldap_Exception
1055      */
1056     public function getParent(Zend_Ldap $ldap = null)
1057     {
1058         if ($ldap !== null) {
1059             $this->attachLdap($ldap);
1060         }
1061         $ldap = $this->getLdap();
1062         $parentDn = $this->_getDn()->getParentDn(1);
1063         return self::fromLdap($parentDn, $ldap);
1064     }
1065 
1066     /**
1067      * Return the current attribute.
1068      * Implements Iterator
1069      *
1070      * @return array
1071      */
1072     public function current()
1073     {
1074         return $this;
1075     }
1076 
1077     /**
1078      * Return the attribute name.
1079      * Implements Iterator
1080      *
1081      * @return string
1082      */
1083     public function key()
1084     {
1085         return $this->getRdnString();
1086     }
1087 
1088     /**
1089      * Move forward to next attribute.
1090      * Implements Iterator
1091      */
1092     public function next()
1093     {
1094         $this->_iteratorRewind = false;
1095     }
1096 
1097     /**
1098      * Rewind the Iterator to the first attribute.
1099      * Implements Iterator
1100      */
1101     public function rewind()
1102     {
1103         $this->_iteratorRewind = true;
1104     }
1105 
1106     /**
1107      * Check if there is a current attribute
1108      * after calls to rewind() or next().
1109      * Implements Iterator
1110      *
1111      * @return boolean
1112      */
1113     public function valid()
1114     {
1115         return $this->_iteratorRewind;
1116     }
1117 
1118     ####################################################
1119     # Empty method bodies for overriding in subclasses #
1120     ####################################################
1121 
1122     /**
1123      * Allows pre-delete logic to be applied to node.
1124      * Subclasses may override this method.
1125      *
1126      * @return void
1127      */
1128     protected function _preDelete() { }
1129 
1130     /**
1131      * Allows post-delete logic to be applied to node.
1132      * Subclasses may override this method.
1133      *
1134      * @return void
1135      */
1136     protected function _postDelete() { }
1137 
1138     /**
1139      * Allows pre-add logic to be applied to node.
1140      * Subclasses may override this method.
1141      *
1142      * @return void
1143      */
1144     protected function _preAdd() { }
1145 
1146     /**
1147      * Allows post-add logic to be applied to node.
1148      * Subclasses may override this method.
1149      *
1150      * @return void
1151      */
1152     protected function _postAdd() { }
1153 
1154     /**
1155      * Allows pre-rename logic to be applied to node.
1156      * Subclasses may override this method.
1157      *
1158      * @return void
1159      */
1160     protected function _preRename() { }
1161 
1162     /**
1163      * Allows post-rename logic to be applied to node.
1164      * Subclasses may override this method.
1165      *
1166      * @return void
1167      */
1168     protected function _postRename() { }
1169 
1170     /**
1171      * Allows pre-update logic to be applied to node.
1172      * Subclasses may override this method.
1173      *
1174      * @return void
1175      */
1176     protected function _preUpdate() { }
1177 
1178     /**
1179      * Allows post-update logic to be applied to node.
1180      * Subclasses may override this method.
1181      *
1182      * @return void
1183      */
1184     protected function _postUpdate() { }
1185 }