Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4: */
3
/**
4
* File containing the Net_LDAP2_Entry interface class.
5
*
6
* PHP version 5
7
*
8
* @category  Net
9
* @package   Net_LDAP2
10
* @author    Jan Wagner <wagner@netsols.de>
11
* @author    Tarjej Huse <tarjei@bergfald.no>
12
* @author    Benedikt Hallinger <beni@php.net>
13
* @copyright 2009 Tarjej Huse, Jan Wagner, Benedikt Hallinger
14
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
15
* @version   SVN: $Id: Entry.php 307580 2011-01-19 12:32:05Z beni $
16
* @link      http://pear.php.net/package/Net_LDAP2/
17
*/
18
 
19
/**
20
* Includes
21
*/
22
require_once 'PEAR.php';
23
require_once 'Net/LDAP2/Util.php';
24
 
25
/**
26
* Object representation of a directory entry
27
*
28
* This class represents a directory entry. You can add, delete, replace
29
* attributes and their values, rename the entry, delete the entry.
30
*
31
* @category Net
32
* @package  Net_LDAP2
33
* @author   Jan Wagner <wagner@netsols.de>
34
* @author   Tarjej Huse <tarjei@bergfald.no>
35
* @author   Benedikt Hallinger <beni@php.net>
36
* @license  http://www.gnu.org/copyleft/lesser.html LGPL
37
* @link     http://pear.php.net/package/Net_LDAP2/
38
*/
39
class Net_LDAP2_Entry extends PEAR
40
{
41
    /**
42
    * Entry ressource identifier
43
    *
44
    * @access protected
45
    * @var ressource
46
    */
47
    protected $_entry = null;
48
 
49
    /**
50
    * LDAP ressource identifier
51
    *
52
    * @access protected
53
    * @var ressource
54
    */
55
    protected $_link = null;
56
 
57
    /**
58
    * Net_LDAP2 object
59
    *
60
    * This object will be used for updating and schema checking
61
    *
62
    * @access protected
63
    * @var object Net_LDAP2
64
    */
65
    protected $_ldap = null;
66
 
67
    /**
68
    * Distinguished name of the entry
69
    *
70
    * @access protected
71
    * @var string
72
    */
73
    protected $_dn = null;
74
 
75
    /**
76
    * Attributes
77
    *
78
    * @access protected
79
    * @var array
80
    */
81
    protected $_attributes = array();
82
 
83
    /**
84
    * Original attributes before any modification
85
    *
86
    * @access protected
87
    * @var array
88
    */
89
    protected $_original = array();
90
 
91
 
92
    /**
93
    * Map of attribute names
94
    *
95
    * @access protected
96
    * @var array
97
    */
98
    protected $_map = array();
99
 
100
 
101
    /**
102
    * Is this a new entry?
103
    *
104
    * @access protected
105
    * @var boolean
106
    */
107
    protected $_new = true;
108
 
109
    /**
110
    * New distinguished name
111
    *
112
    * @access protected
113
    * @var string
114
    */
115
    protected $_newdn = null;
116
 
117
    /**
118
    * Shall the entry be deleted?
119
    *
120
    * @access protected
121
    * @var boolean
122
    */
123
    protected $_delete = false;
124
 
125
    /**
126
    * Map with changes to the entry
127
    *
128
    * @access protected
129
    * @var array
130
    */
131
    protected $_changes = array("add"     => array(),
132
                                "delete"  => array(),
133
                                "replace" => array()
134
                               );
135
    /**
136
    * Internal Constructor
137
    *
138
    * Constructor of the entry. Sets up the distinguished name and the entries
139
    * attributes.
140
    * You should not call this method manually! Use {@link Net_LDAP2_Entry::createFresh()}
141
    * or {@link Net_LDAP2_Entry::createConnected()} instead!
142
    *
143
    * @param Net_LDAP2|ressource|array &$ldap Net_LDAP2 object, ldap-link ressource or array of attributes
144
    * @param string|ressource          $entry Either a DN or a LDAP-Entry ressource
145
    *
146
    * @access protected
147
    * @return none
148
    */
149
    protected function __construct(&$ldap, $entry = null)
150
    {
151
        $this->PEAR('Net_LDAP2_Error');
152
 
153
        // set up entry resource or DN
154
        if (is_resource($entry)) {
155
            $this->_entry = &$entry;
156
        } else {
157
            $this->_dn = $entry;
158
        }
159
 
160
        // set up LDAP link
161
        if ($ldap instanceof Net_LDAP2) {
162
            $this->_ldap = &$ldap;
163
            $this->_link = $ldap->getLink();
164
        } elseif (is_resource($ldap)) {
165
            $this->_link = $ldap;
166
        } elseif (is_array($ldap)) {
167
            // Special case: here $ldap is an array of attributes,
168
            // this means, we have no link. This is a "virtual" entry.
169
            // We just set up the attributes so one can work with the object
170
            // as expected, but an update() fails unless setLDAP() is called.
171
            $this->setAttributes($ldap);
172
        }
173
 
174
        // if this is an entry existing in the directory,
175
        // then set up as old and fetch attrs
176
        if (is_resource($this->_entry) && is_resource($this->_link)) {
177
            $this->_new = false;
178
            $this->_dn  = @ldap_get_dn($this->_link, $this->_entry);
179
            $this->setAttributes();  // fetch attributes from server
180
        }
181
    }
182
 
183
    /**
184
    * Creates a fresh entry that may be added to the directory later on
185
    *
186
    * Use this method, if you want to initialize a fresh entry.
187
    *
188
    * The method should be called statically: $entry = Net_LDAP2_Entry::createFresh();
189
    * You should put a 'objectClass' attribute into the $attrs so the directory server
190
    * knows which object you want to create. However, you may omit this in case you
191
    * don't want to add this entry to a directory server.
192
    *
193
    * The attributes parameter is as following:
194
    * <code>
195
    * $attrs = array( 'attribute1' => array('value1', 'value2'),
196
    *                 'attribute2' => 'single value'
197
    *          );
198
    * </code>
199
    *
200
    * @param string $dn    DN of the Entry
201
    * @param array  $attrs Attributes of the entry
202
    *
203
    * @static
204
    * @return Net_LDAP2_Entry|Net_LDAP2_Error
205
    */
206
    public static function createFresh($dn, $attrs = array())
207
    {
208
        if (!is_array($attrs)) {
209
            return PEAR::raiseError("Unable to create fresh entry: Parameter \$attrs needs to be an array!");
210
        }
211
 
212
        $entry = new Net_LDAP2_Entry($attrs, $dn);
213
        return $entry;
214
    }
215
 
216
    /**
217
    * Creates a Net_LDAP2_Entry object out of an ldap entry resource
218
    *
219
    * Use this method, if you want to initialize an entry object that is
220
    * already present in some directory and that you have read manually.
221
    *
222
    * Please note, that if you want to create an entry object that represents
223
    * some already existing entry, you should use {@link createExisting()}.
224
    *
225
    * The method should be called statically: $entry = Net_LDAP2_Entry::createConnected();
226
    *
227
    * @param Net_LDAP2 $ldap  Net_LDA2 object
228
    * @param resource  $entry PHP LDAP entry resource
229
    *
230
    * @static
231
    * @return Net_LDAP2_Entry|Net_LDAP2_Error
232
    */
233
    public static function createConnected($ldap, $entry)
234
    {
235
        if (!$ldap instanceof Net_LDAP2) {
236
            return PEAR::raiseError("Unable to create connected entry: Parameter \$ldap needs to be a Net_LDAP2 object!");
237
        }
238
        if (!is_resource($entry)) {
239
            return PEAR::raiseError("Unable to create connected entry: Parameter \$entry needs to be a ldap entry resource!");
240
        }
241
 
242
        $entry = new Net_LDAP2_Entry($ldap, $entry);
243
        return $entry;
244
    }
245
 
246
    /**
247
    * Creates an Net_LDAP2_Entry object that is considered already existing
248
    *
249
    * Use this method, if you want to modify an already existing entry
250
    * without fetching it first.
251
    * In most cases however, it is better to fetch the entry via Net_LDAP2->getEntry()!
252
    *
253
    * Please note that you should take care if you construct entries manually with this
254
    * because you may get weird synchronisation problems.
255
    * The attributes and values as well as the entry itself are considered existent
256
    * which may produce errors if you try to modify an entry which doesn't really exist
257
    * or if you try to overwrite some attribute with an value already present.
258
    *
259
    * This method is equal to calling createFresh() and after that markAsNew(FALSE).
260
    *
261
    * The method should be called statically: $entry = Net_LDAP2_Entry::createExisting();
262
    *
263
    * The attributes parameter is as following:
264
    * <code>
265
    * $attrs = array( 'attribute1' => array('value1', 'value2'),
266
    *                 'attribute2' => 'single value'
267
    *          );
268
    * </code>
269
    *
270
    * @param string $dn    DN of the Entry
271
    * @param array  $attrs Attributes of the entry
272
    *
273
    * @static
274
    * @return Net_LDAP2_Entry|Net_LDAP2_Error
275
    */
276
    public static function createExisting($dn, $attrs = array())
277
    {
278
        if (!is_array($attrs)) {
279
            return PEAR::raiseError("Unable to create entry object: Parameter \$attrs needs to be an array!");
280
        }
281
 
282
        $entry = Net_LDAP2_Entry::createFresh($dn, $attrs);
283
        if ($entry instanceof Net_LDAP2_Error) {
284
            return $entry;
285
        } else {
286
            $entry->markAsNew(false);
287
            return $entry;
288
        }
289
    }
290
 
291
    /**
292
    * Get or set the distinguished name of the entry
293
    *
294
    * If called without an argument the current (or the new DN if set) DN gets returned.
295
    * If you provide an DN, this entry is moved to the new location specified if a DN existed.
296
    * If the DN was not set, the DN gets initialized. Call {@link update()} to actually create
297
    * the new Entry in the directory.
298
    * To fetch the current active DN after setting a new DN but before an update(), you can use
299
    * {@link currentDN()} to retrieve the DN that is currently active.
300
    *
301
    * Please note that special characters (eg german umlauts) should be encoded using utf8_encode().
302
    * You may use {@link Net_LDAP2_Util::canonical_dn()} for properly encoding of the DN.
303
    *
304
    * @param string $dn New distinguished name
305
    *
306
    * @access public
307
    * @return string|true Distinguished name (or true if a new DN was provided)
308
    */
309
    public function dn($dn = null)
310
    {
311
        if (false == is_null($dn)) {
312
            if (is_null($this->_dn)) {
313
                $this->_dn = $dn;
314
            } else {
315
                $this->_newdn = $dn;
316
            }
317
            return true;
318
        }
319
        return (isset($this->_newdn) ? $this->_newdn : $this->currentDN());
320
    }
321
 
322
    /**
323
    * Renames or moves the entry
324
    *
325
    * This is just a convinience alias to {@link dn()}
326
    * to make your code more meaningful.
327
    *
328
    * @param string $newdn The new DN
329
    *
330
    * @return true
331
    */
332
    public function move($newdn)
333
    {
334
        return $this->dn($newdn);
335
    }
336
 
337
    /**
338
    * Sets the internal attributes array
339
    *
340
    * This fetches the values for the attributes from the server.
341
    * The attribute Syntax will be checked so binary attributes will be returned
342
    * as binary values.
343
    *
344
    * Attributes may be passed directly via the $attributes parameter to setup this
345
    * entry manually. This overrides attribute fetching from the server.
346
    *
347
    * @param array $attributes Attributes to set for this entry
348
    *
349
    * @access protected
350
    * @return void
351
    */
352
    protected function setAttributes($attributes = null)
353
    {
354
        /*
355
        * fetch attributes from the server
356
        */
357
        if (is_null($attributes) && is_resource($this->_entry) && is_resource($this->_link)) {
358
            // fetch schema
359
            if ($this->_ldap instanceof Net_LDAP2) {
360
                $schema =& $this->_ldap->schema();
361
            }
362
            // fetch attributes
363
            $attributes = array();
364
            do {
365
                if (empty($attr)) {
366
                    $ber  = null;
367
                    $attr = @ldap_first_attribute($this->_link, $this->_entry, $ber);
368
                } else {
369
                    $attr = @ldap_next_attribute($this->_link, $this->_entry, $ber);
370
                }
371
                if ($attr) {
372
                    $func = 'ldap_get_values'; // standard function to fetch value
373
 
374
                    // Try to get binary values as binary data
375
                    if ($schema instanceof Net_LDAP2_Schema) {
376
                        if ($schema->isBinary($attr)) {
377
                             $func = 'ldap_get_values_len';
378
                        }
379
                    }
380
                    // fetch attribute value (needs error checking?)
381
                    $attributes[$attr] = $func($this->_link, $this->_entry, $attr);
382
                }
383
            } while ($attr);
384
        }
385
 
386
        /*
387
        * set attribute data directly, if passed
388
        */
389
        if (is_array($attributes) && count($attributes) > 0) {
390
            if (isset($attributes["count"]) && is_numeric($attributes["count"])) {
391
                unset($attributes["count"]);
392
            }
393
            foreach ($attributes as $k => $v) {
394
                // attribute names should not be numeric
395
                if (is_numeric($k)) {
396
                    continue;
397
                }
398
                // map generic attribute name to real one
399
                $this->_map[strtolower($k)] = $k;
400
                // attribute values should be in an array
401
                if (false == is_array($v)) {
402
                    $v = array($v);
403
                }
404
                // remove the value count (comes from ldap server)
405
                if (isset($v["count"])) {
406
                    unset($v["count"]);
407
                }
408
                $this->_attributes[$k] = $v;
409
            }
410
        }
411
 
412
        // save a copy for later use
413
        $this->_original = $this->_attributes;
414
    }
415
 
416
    /**
417
    * Get the values of all attributes in a hash
418
    *
419
    * The returned hash has the form
420
    * <code>array('attributename' => 'single value',
421
    *       'attributename' => array('value1', value2', value3'))</code>
422
    * Only attributes present at the entry will be returned.
423
    *
424
    * @access public
425
    * @return array Hash of all attributes with their values
426
    */
427
    public function getValues()
428
    {
429
        $attrs = array();
430
        foreach ($this->_attributes as $attr => $value) {
431
            $attrs[$attr] = $this->getValue($attr);
432
        }
433
        return $attrs;
434
    }
435
 
436
    /**
437
    * Get the value of a specific attribute
438
    *
439
    * The first parameter is the name of the attribute
440
    * The second parameter influences the way the value is returned:
441
    * 'single':  only the first value is returned as string
442
    * 'all':     all values including the value count are returned in an array
443
    * 'default': in all other cases an attribute value with a single value is
444
    *            returned as string, if it has multiple values it is returned
445
    *            as an array (without value count)
446
    *
447
    * If the attribute is not set at this entry (no value or not defined in
448
    * schema), an empty string is returned.
449
    *
450
    * You may use Net_LDAP2_Schema->checkAttribute() to see if the attribute
451
    * is defined for the objectClasses of this entry.
452
    *
453
    * @param string  $attr         Attribute name
454
    * @param string  $option       Option
455
    *
456
    * @access public
457
    * @return string|array|PEAR_Error string, array or PEAR_Error
458
    */
459
    public function getValue($attr, $option = null)
460
    {
461
        $attr = $this->getAttrName($attr);
462
 
463
        // If the attribute is not set at the entry, return an empty value.
464
        // Users should do schema checks if they want to know if an attribute is
465
        // valid for an entrys OCLs.
466
        if (!array_key_exists($attr, $this->_attributes)) {
467
            $value = array('');
468
        } else {
469
            $value = $this->_attributes[$attr];
470
        }
471
 
472
        // format the attribute values depending on $option
473
        if ($option == "single" || (count($value) == 1 && $option != 'all')) {
474
            $value = array_shift($value);
475
        }
476
 
477
        return $value;
478
    }
479
 
480
    /**
481
    * Alias function of getValue for perl-ldap interface
482
    *
483
    * @see getValue()
484
    * @return string|array|PEAR_Error
485
    */
486
    public function get_value()
487
    {
488
        $args = func_get_args();
489
        return call_user_func_array(array( &$this, 'getValue' ), $args);
490
    }
491
 
492
    /**
493
    * Returns an array of attributes names
494
    *
495
    * @access public
496
    * @return array Array of attribute names
497
    */
498
    public function attributes()
499
    {
500
        return array_keys($this->_attributes);
501
    }
502
 
503
    /**
504
    * Returns whether an attribute exists or not
505
    *
506
    * @param string $attr Attribute name
507
    *
508
    * @access public
509
    * @return boolean
510
    */
511
    public function exists($attr)
512
    {
513
        $attr = $this->getAttrName($attr);
514
        return array_key_exists($attr, $this->_attributes);
515
    }
516
 
517
    /**
518
    * Adds a new attribute or a new value to an existing attribute
519
    *
520
    * The paramter has to be an array of the form:
521
    * array('attributename' => 'single value',
522
    *       'attributename' => array('value1', 'value2))
523
    * When the attribute already exists the values will be added, else the
524
    * attribute will be created. These changes are local to the entry and do
525
    * not affect the entry on the server until update() is called.
526
    *
527
    * Note, that you can add values of attributes that you haven't selected, but if
528
    * you do so, {@link getValue()} and {@link getValues()} will only return the
529
    * values you added, _NOT_ all values present on the server. To avoid this, just refetch
530
    * the entry after calling {@link update()} or select the attribute.
531
    *
532
    * @param array $attr Attributes to add
533
    *
534
    * @access public
535
    * @return true|Net_LDAP2_Error
536
    */
537
    public function add($attr = array())
538
    {
539
        if (false == is_array($attr)) {
540
            return PEAR::raiseError("Parameter must be an array");
541
        }
542
        if ($this->isNew()) {
543
            $this->setAttributes($attr);
544
        } else {
545
            foreach ($attr as $k => $v) {
546
                $k = $this->getAttrName($k);
547
                if (false == is_array($v)) {
548
                    // Do not add empty values
549
                    if ($v == null) {
550
                        continue;
551
                    } else {
552
                        $v = array($v);
553
                    }
554
                }
555
                // add new values to existing attribute or add new attribute
556
                if ($this->exists($k)) {
557
                    $this->_attributes[$k] = array_unique(array_merge($this->_attributes[$k], $v));
558
                } else {
559
                    $this->_map[strtolower($k)] = $k;
560
                    $this->_attributes[$k]      = $v;
561
                }
562
                // save changes for update()
563
                if (empty($this->_changes["add"][$k])) {
564
                    $this->_changes["add"][$k] = array();
565
                }
566
                $this->_changes["add"][$k] = array_unique(array_merge($this->_changes["add"][$k], $v));
567
            }
568
        }
569
        $return = true;
570
        return $return;
571
    }
572
 
573
    /**
574
    * Deletes an whole attribute or a value or the whole entry
575
    *
576
    * The parameter can be one of the following:
577
    *
578
    * "attributename" - The attribute as a whole will be deleted
579
    * array("attributename1", "attributename2) - All given attributes will be
580
    *                                            deleted
581
    * array("attributename" => "value") - The value will be deleted
582
    * array("attributename" => array("value1", "value2") - The given values
583
    *                                                      will be deleted
584
    * If $attr is null or omitted , then the whole Entry will be deleted!
585
    *
586
    * These changes are local to the entry and do
587
    * not affect the entry on the server until {@link update()} is called.
588
    *
589
    * Please note that you must select the attribute (at $ldap->search() for example)
590
    * to be able to delete values of it, Otherwise {@link update()} will silently fail
591
    * and remove nothing.
592
    *
593
    * @param string|array $attr Attributes to delete (NULL or missing to delete whole entry)
594
    *
595
    * @access public
596
    * @return true
597
    */
598
    public function delete($attr = null)
599
    {
600
        if (is_null($attr)) {
601
            $this->_delete = true;
602
            return true;
603
        }
604
        if (is_string($attr)) {
605
            $attr = array($attr);
606
        }
607
        // Make the assumption that attribute names cannot be numeric,
608
        // therefore this has to be a simple list of attribute names to delete
609
        if (is_numeric(key($attr))) {
610
            foreach ($attr as $name) {
611
                if (is_array($name)) {
612
                    // someone mixed modes (list mode but specific values given!)
613
                    $del_attr_name = array_search($name, $attr);
614
                    $this->delete(array($del_attr_name => $name));
615
                } else {
616
                    // mark for update() if this attr was not marked before
617
                    $name = $this->getAttrName($name);
618
                    if ($this->exists($name)) {
619
                        $this->_changes["delete"][$name] = null;
620
                        unset($this->_attributes[$name]);
621
                    }
622
                }
623
            }
624
        } else {
625
            // Here we have a hash with "attributename" => "value to delete"
626
            foreach ($attr as $name => $values) {
627
                if (is_int($name)) {
628
                    // someone mixed modes and gave us just an attribute name
629
                    $this->delete($values);
630
                } else {
631
                    // mark for update() if this attr was not marked before;
632
                    // this time it must consider the selected values also
633
                    $name = $this->getAttrName($name);
634
                    if ($this->exists($name)) {
635
                        if (false == is_array($values)) {
636
                            $values = array($values);
637
                        }
638
                        // save values to be deleted
639
                        if (empty($this->_changes["delete"][$name])) {
640
                            $this->_changes["delete"][$name] = array();
641
                        }
642
                        $this->_changes["delete"][$name] =
643
                            array_unique(array_merge($this->_changes["delete"][$name], $values));
644
                        foreach ($values as $value) {
645
                            // find the key for the value that should be deleted
646
                            $key = array_search($value, $this->_attributes[$name]);
647
                            if (false !== $key) {
648
                                // delete the value
649
                                unset($this->_attributes[$name][$key]);
650
                            }
651
                        }
652
                    }
653
                }
654
            }
655
        }
656
        $return = true;
657
        return $return;
658
    }
659
 
660
    /**
661
    * Replaces attributes or its values
662
    *
663
    * The parameter has to an array of the following form:
664
    * array("attributename" => "single value",
665
    *       "attribute2name" => array("value1", "value2"),
666
    *       "deleteme1" => null,
667
    *       "deleteme2" => "")
668
    * If the attribute does not yet exist it will be added instead (see also $force).
669
    * If the attribue value is null, the attribute will de deleted.
670
    *
671
    * These changes are local to the entry and do
672
    * not affect the entry on the server until {@link update()} is called.
673
    *
674
    * In some cases you are not allowed to read the attributes value (for
675
    * example the ActiveDirectory attribute unicodePwd) but are allowed to
676
    * replace the value. In this case replace() would assume that the attribute
677
    * is not in the directory yet and tries to add it which will result in an
678
    * LDAP_TYPE_OR_VALUE_EXISTS error.
679
    * To force replace mode instead of add, you can set $force to true.
680
    *
681
    * @param array $attr  Attributes to replace
682
    * @param bool  $force Force replacing mode in case we can't read the attr value but are allowed to replace it
683
    *
684
    * @access public
685
    * @return true|Net_LDAP2_Error
686
    */
687
    public function replace($attr = array(), $force = false)
688
    {
689
        if (false == is_array($attr)) {
690
            return PEAR::raiseError("Parameter must be an array");
691
        }
692
        foreach ($attr as $k => $v) {
693
            $k = $this->getAttrName($k);
694
            if (false == is_array($v)) {
695
                // delete attributes with empty values; treat ints as string
696
                if (is_int($v)) {
697
                    $v = "$v";
698
                }
699
                if ($v == null) {
700
                    $this->delete($k);
701
                    continue;
702
                } else {
703
                    $v = array($v);
704
                }
705
            }
706
            // existing attributes will get replaced
707
            if ($this->exists($k) || $force) {
708
                $this->_changes["replace"][$k] = $v;
709
                $this->_attributes[$k]         = $v;
710
            } else {
711
                // new ones just get added
712
                $this->add(array($k => $v));
713
            }
714
        }
715
        $return = true;
716
        return $return;
717
    }
718
 
719
    /**
720
    * Update the entry on the directory server
721
    *
722
    * This will evaluate all changes made so far and send them
723
    * to the directory server.
724
    * Please note, that if you make changes to objectclasses wich
725
    * have mandatory attributes set, update() will currently fail.
726
    * Remove the entry from the server and readd it as new in such cases.
727
    * This also will deal with problems with setting structural object classes.
728
    *
729
    * @param Net_LDAP2 $ldap If passed, a call to setLDAP() is issued prior update, thus switching the LDAP-server. This is for perl-ldap interface compliance
730
    *
731
    * @access public
732
    * @return true|Net_LDAP2_Error
733
    * @todo Entry rename with a DN containing special characters needs testing!
734
    */
735
    public function update($ldap = null)
736
    {
737
        if ($ldap) {
738
            $msg = $this->setLDAP($ldap);
739
            if (Net_LDAP2::isError($msg)) {
740
                return PEAR::raiseError('You passed an invalid $ldap variable to update()');
741
            }
742
        }
743
 
744
        // ensure we have a valid LDAP object
745
        $ldap =& $this->getLDAP();
746
        if (!$ldap instanceof Net_LDAP2) {
747
            return PEAR::raiseError("The entries LDAP object is not valid");
748
        }
749
 
750
        // Get and check link
751
        $link = $ldap->getLink();
752
        if (!is_resource($link)) {
753
            return PEAR::raiseError("Could not update entry: internal LDAP link is invalid");
754
        }
755
 
756
        /*
757
        * Delete the entry
758
        */
759
        if (true === $this->_delete) {
760
            return $ldap->delete($this);
761
        }
762
 
763
        /*
764
        * New entry
765
        */
766
        if (true === $this->_new) {
767
            $msg = $ldap->add($this);
768
            if (Net_LDAP2::isError($msg)) {
769
                return $msg;
770
            }
771
            $this->_new                = false;
772
            $this->_changes['add']     = array();
773
            $this->_changes['delete']  = array();
774
            $this->_changes['replace'] = array();
775
            $this->_original           = $this->_attributes;
776
 
777
            $return = true;
778
            return $return;
779
        }
780
 
781
        /*
782
        * Rename/move entry
783
        */
784
        if (false == is_null($this->_newdn)) {
785
            if ($ldap->getLDAPVersion() !== 3) {
786
                return PEAR::raiseError("Renaming/Moving an entry is only supported in LDAPv3");
787
            }
788
            // make dn relative to parent (needed for ldap rename)
789
            $parent = Net_LDAP2_Util::ldap_explode_dn($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false));
790
            if (Net_LDAP2::isError($parent)) {
791
                return $parent;
792
            }
793
            $child = array_shift($parent);
794
            // maybe the dn consist of a multivalued RDN, we must build the dn in this case
795
            // because the $child-RDN is an array!
796
            if (is_array($child)) {
797
                $child = Net_LDAP2_Util::canonical_dn($child);
798
            }
799
            $parent = Net_LDAP2_Util::canonical_dn($parent);
800
 
801
            // rename/move
802
            if (false == @ldap_rename($link, $this->_dn, $child, $parent, true)) {
803
                return PEAR::raiseError("Entry not renamed: " .
804
                                        @ldap_error($link), @ldap_errno($link));
805
            }
806
            // reflect changes to local copy
807
            $this->_dn    = $this->_newdn;
808
            $this->_newdn = null;
809
        }
810
 
811
        /*
812
        * Carry out modifications to the entry
813
        */
814
        // ADD
815
        foreach ($this->_changes["add"] as $attr => $value) {
816
            // if attribute exists, add new values
817
            if ($this->exists($attr)) {
818
                if (false === @ldap_mod_add($link, $this->dn(), array($attr => $value))) {
819
                    return PEAR::raiseError("Could not add new values to attribute $attr: " .
820
                                            @ldap_error($link), @ldap_errno($link));
821
                }
822
            } else {
823
                // new attribute
824
                if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) {
825
                    return PEAR::raiseError("Could not add new attribute $attr: " .
826
                                            @ldap_error($link), @ldap_errno($link));
827
                }
828
            }
829
            // all went well here, I guess
830
            unset($this->_changes["add"][$attr]);
831
        }
832
 
833
        // DELETE
834
        foreach ($this->_changes["delete"] as $attr => $value) {
835
            // In LDAPv3 you need to specify the old values for deleting
836
            if (is_null($value) && $ldap->getLDAPVersion() === 3) {
837
                $value = $this->_original[$attr];
838
            }
839
            if (false === @ldap_mod_del($link, $this->dn(), array($attr => $value))) {
840
                return PEAR::raiseError("Could not delete attribute $attr: " .
841
                                        @ldap_error($link), @ldap_errno($link));
842
            }
843
            unset($this->_changes["delete"][$attr]);
844
        }
845
 
846
        // REPLACE
847
        foreach ($this->_changes["replace"] as $attr => $value) {
848
            if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) {
849
                return PEAR::raiseError("Could not replace attribute $attr values: " .
850
                                        @ldap_error($link), @ldap_errno($link));
851
            }
852
            unset($this->_changes["replace"][$attr]);
853
        }
854
 
855
        // all went well, so _original (server) becomes _attributes (local copy)
856
        $this->_original = $this->_attributes;
857
 
858
        $return = true;
859
        return $return;
860
    }
861
 
862
    /**
863
    * Returns the right attribute name
864
    *
865
    * @param string $attr Name of attribute
866
    *
867
    * @access protected
868
    * @return string The right name of the attribute
869
    */
870
    protected function getAttrName($attr)
871
    {
872
        $name = strtolower($attr);
873
        if (array_key_exists($name, $this->_map)) {
874
            $attr = $this->_map[$name];
875
        }
876
        return $attr;
877
    }
878
 
879
    /**
880
    * Returns a reference to the LDAP-Object of this entry
881
    *
882
    * @access public
883
    * @return Net_LDAP2|Net_LDAP2_Error   Reference to the Net_LDAP2 Object (the connection) or Net_LDAP2_Error
884
    */
885
    public function &getLDAP()
886
    {
887
        if (!$this->_ldap instanceof Net_LDAP2) {
888
            $err = new PEAR_Error('LDAP is not a valid Net_LDAP2 object');
889
            return $err;
890
        } else {
891
            return $this->_ldap;
892
        }
893
    }
894
 
895
    /**
896
    * Sets a reference to the LDAP-Object of this entry
897
    *
898
    * After setting a Net_LDAP2 object, calling update() will use that object for
899
    * updating directory contents. Use this to dynamicly switch directorys.
900
    *
901
    * @param Net_LDAP2 &$ldap Net_LDAP2 object that this entry should be connected to
902
    *
903
    * @access public
904
    * @return true|Net_LDAP2_Error
905
    */
906
    public function setLDAP(&$ldap)
907
    {
908
        if (!$ldap instanceof Net_LDAP2) {
909
            return PEAR::raiseError("LDAP is not a valid Net_LDAP2 object");
910
        } else {
911
            $this->_ldap =& $ldap;
912
            return true;
913
        }
914
    }
915
 
916
    /**
917
    * Marks the entry as new/existing.
918
    *
919
    * If an Entry is marked as new, it will be added to the directory
920
    * when calling {@link update()}.
921
    * If the entry is marked as old ($mark = false), then the entry is
922
    * assumed to be present in the directory server wich results in
923
    * modification when calling {@link update()}.
924
    *
925
    * @param boolean $mark Value to set, defaults to "true"
926
    *
927
    * @return void
928
    */
929
    public function markAsNew($mark = true)
930
    {
931
        $this->_new = ($mark)? true : false;
932
    }
933
 
934
    /**
935
    * Applies a regular expression onto a single- or multivalued attribute (like preg_match())
936
    *
937
    * This method behaves like PHPs preg_match() but with some exceptions.
938
    * If you want to retrieve match information, then you MUST pass the
939
    * $matches parameter via reference! otherwise you will get no matches.
940
    * Since it is possible to have multi valued attributes the $matches
941
    * array will have a additionally numerical dimension (one for each value):
942
    * <code>
943
    * $matches = array(
944
    *         0 => array (usual preg_match() returnarray),
945
    *         1 => array (usual preg_match() returnarray)
946
    *     )
947
    * </code>
948
    * Please note, that $matches will be initialized to an empty array inside.
949
    *
950
    * Usage example:
951
    * <code>
952
    * $result = $entry->preg_match('/089(\d+)/', 'telephoneNumber', &$matches);
953
    * if ( $result === true ){
954
    *     echo "First match: ".$matches[0][1];   // Match of value 1, content of first bracket
955
    * } else {
956
    *     if ( Net_LDAP2::isError($result) ) {
957
    *         echo "Error: ".$result->getMessage();
958
    *     } else {
959
    *         echo "No match found.";
960
    *     }
961
    * }
962
    * </code>
963
    *
964
    * Please note that it is important to test for an Net_LDAP2_Error, because objects are
965
    * evaluating to true by default, thus if an error occured, and you only check using "==" then
966
    * you get misleading results. Use the "identical" (===) operator to test for matches to
967
    * avoid this as shown above.
968
    *
969
    * @param string $regex     The regular expression
970
    * @param string $attr_name The attribute to search in
971
    * @param array  $matches   (optional, PASS BY REFERENCE!) Array to store matches in
972
    *
973
    * @return boolean|Net_LDAP2_Error  TRUE, if we had a match in one of the values, otherwise false. Net_LDAP2_Error in case something went wrong
974
    */
975
    public function pregMatch($regex, $attr_name, $matches = array())
976
    {
977
        $matches = array();
978
 
979
        // fetch attribute values
980
        $attr = $this->getValue($attr_name, 'all');
981
        if (Net_LDAP2::isError($attr)) {
982
            return $attr;
983
        } else {
984
            unset($attr['count']);
985
        }
986
 
987
        // perform preg_match() on all values
988
        $match = false;
989
        foreach ($attr as $thisvalue) {
990
            $matches_int = array();
991
            if (preg_match($regex, $thisvalue, $matches_int)) {
992
                $match = true;
993
                array_push($matches, $matches_int); // store matches in reference
994
            }
995
        }
996
        return $match;
997
    }
998
 
999
    /**
1000
    * Alias of {@link pregMatch()} for compatibility to Net_LDAP 1
1001
    *
1002
    * @see pregMatch()
1003
    * @return boolean|Net_LDAP2_Error
1004
    */
1005
    public function preg_match()
1006
    {
1007
        $args = func_get_args();
1008
        return call_user_func_array(array( &$this, 'pregMatch' ), $args);
1009
    }
1010
 
1011
    /**
1012
    * Tells if the entry is consiedered as new (not present in the server)
1013
    *
1014
    * Please note, that this doesn't tell you if the entry is present on the server.
1015
    * Use {@link Net_LDAP2::dnExists()} to see if an entry is already there.
1016
    *
1017
    * @return boolean
1018
    */
1019
    public function isNew()
1020
    {
1021
        return $this->_new;
1022
    }
1023
 
1024
 
1025
    /**
1026
    * Is this entry going to be deleted once update() is called?
1027
    *
1028
    * @return boolean
1029
    */
1030
    public function willBeDeleted()
1031
    {
1032
        return $this->_delete;
1033
    }
1034
 
1035
    /**
1036
    * Is this entry going to be moved once update() is called?
1037
    *
1038
    * @return boolean
1039
    */
1040
    public function willBeMoved()
1041
    {
1042
        return ($this->dn() !== $this->currentDN());
1043
    }
1044
 
1045
    /**
1046
    * Returns always the original DN
1047
    *
1048
    * If an entry will be moved but {@link update()} was not called,
1049
    * {@link dn()} will return the new DN. This method however, returns
1050
    * always the current active DN.
1051
    *
1052
    * @return string
1053
    */
1054
    public function currentDN()
1055
    {
1056
        return $this->_dn;
1057
    }
1058
 
1059
    /**
1060
    * Returns the attribute changes to be carried out once update() is called
1061
    *
1062
    * @return array
1063
    */
1064
    public function getChanges()
1065
    {
1066
        return $this->_changes;
1067
    }
1068
}
1069
?>