Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1173 jp_milcent 1
<?php
2
//
3
// Pear DB LDAP - Database independent query interface definition
4
// for PHP's LDAP extension.
5
//
6
// Copyright (c) 2002-2003 Ludovico Magnocavallo <ludo@sumatrasolutions.com>
7
//
8
//  This library is free software; you can redistribute it and/or
9
//  modify it under the terms of the GNU Lesser General Public
10
//  License as published by the Free Software Foundation; either
11
//  version 2.1 of the License, or (at your option) any later version.
12
//
13
//  This library is distributed in the hope that it will be useful,
14
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
//  Lesser General Public License for more details.
17
//
18
//  You should have received a copy of the GNU Lesser General Public
19
//  License along with this library; if not, write to the Free Software
20
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21
//
22
// Contributors
23
// - Piotr Roszatycki <dexter@debian.org>
24
//   DB_ldap::base() method, support for LDAP sequences, various fixes
25
// - Aaron Spencer Hawley <aaron dot hawley at uvm dot edu>
26
//   fix to use port number if present in DB_ldap->connect()
27
//
28
// $Id: ldap.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $
29
//
30
 
31
require_once 'DB.php';
32
require_once 'DB/common.php';
33
 
34
define("DB_ERROR_BIND_FAILED",     -26);
35
define("DB_ERROR_UNKNOWN_LDAP_ACTION",     -27);
36
 
37
/**
38
 * LDAP result class
39
 *
40
 * LDAP_result extends DB_result to provide specific LDAP
41
 * result methods.
42
 *
43
 * @version 1.0
44
 * @author Ludovico Magnocavallo <ludo@sumatrasolutions.com>
45
 * @package DB
46
 */
47
 
48
class LDAP_result extends DB_result
49
{
50
 
51
    // {{{ properties
52
 
53
    /**
54
     * data returned from ldap_entries()
55
     * @access private
56
     */
57
    var $_entries   = null;
58
    /**
59
     * result rows as hash of records
60
     * @access private
61
     */
62
    var $_recordset = null;
63
    /**
64
     * current record as hash
65
     * @access private
66
     */
67
    var $_record    = null;
68
 
69
    // }}}
70
    // {{{ constructor
71
 
72
    /**
73
     * class constructor, calls DB_result constructor
74
     * @param ref $dbh reference to the db instance
75
     * @param resource $result ldap command result
76
     */
77
    function LDAP_result(&$dbh, $result)
78
    {
79
        $this->DB_result($dbh, $result);
80
    }
81
 
82
    /**
83
     * fetch rows of data into $this->_recordset
84
     *
85
     * called once as soon as something needs to be returned
86
     * @access private
87
     * @param resource $result ldap command result
88
     * @return boolean true
89
     */
90
    function getRows() {
91
        if ($this->_recordset === null) {
92
            // begin processing result into recordset
93
            $this->_entries = ldap_get_entries($this->dbh->connection, $this->result);
94
            $this->row_counter = $this->_entries['count'];
95
            $i = 1;
96
            $rs_template = array();
97
            if (count($this->dbh->attributes) > 0) {
98
                reset($this->dbh->attributes);
99
                while (list($a_index, $a_name) = each($this->dbh->attributes)) $rs_template[$a_name] = '';
100
            }
101
            while (list($entry_idx, $entry) = each($this->_entries)) {
102
                // begin first loop, iterate through entries
103
                if (!empty($this->dbh->limit_from) && ($i < $this->dbh->limit_from)) continue;
104
                if (!empty($this->dbh->limit_count) && ($i > $this->dbh->limit_count)) break;
105
                $rs = $rs_template;
106
                if (!is_array($entry)) continue;
107
                while (list($attr, $attr_values) = each($entry)) {
108
                    // begin second loop, iterate through attributes
109
                    if (is_int($attr) || $attr == 'count') continue;
110
                    if (is_string($attr_values)) $rs[$attr] = $attr_values;
111
                    else {
112
                        $value = '';
113
                        while (list($value_idx, $attr_value) = each($attr_values)) {
114
                            // begin third loop, iterate through attribute values
115
                            if (!is_int($value_idx)) continue;
116
                            if (empty($value)) $value = $attr_value;
117
                            else {
118
                                if (is_array($value)) $value[] = $attr_value;
119
                                else $value = array($value, $attr_value);
120
                            }
121
//                          else $value .= "\n$attr_value";
122
                            // end third loop
123
                        }
124
                        $rs[$attr] = $value;
125
                    }
126
                    // end second loop
127
                }
128
                reset($rs);
129
                $this->_recordset[$entry_idx] = $rs;
130
                $i++;
131
                // end first loop
132
            }
133
            $this->_entries = null;
134
            if (!is_array($this->_recordset))
135
                $this->_recordset = array();
136
            if (!empty($this->dbh->sorting)) {
137
                $sorting_method = (!empty($this->dbh->sorting_method) ? $this->dbh->sorting_method : 'cmp');
138
                uksort($this->_recordset, array(&$this, $sorting_method));
139
            }
140
            reset($this->_recordset);
141
            // end processing result into recordset
142
        }
143
        return DB_OK;
144
    }
145
 
146
 
147
    /**
148
     * Fetch and return a row of data (it uses driver->fetchInto for that)
149
     * @param int $fetchmode  format of fetched row
150
     * @param int $rownum     the row number to fetch
151
     *
152
     * @return  array a row of data, NULL on no more rows or PEAR_Error on error
153
     *
154
     * @access public
155
     */
156
    function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
157
    {
158
        $this->getRows();
159
        if (count($this->_recordset) == 0) return null;
160
        if ($this->_record !== null) $this->_record = next($this->_recordset);
161
        else $this->_record = current($this->_recordset);
162
        $row = $this->_record;
163
        return $row;
164
    }
165
 
166
 
167
    /**
168
     * Fetch a row of data into an existing variable.
169
     *
170
     * @param  mixed     $arr        reference to data containing the row
171
     * @param  integer   $fetchmode  format of fetched row
172
     * @param  integer   $rownum     the row number to fetch
173
     *
174
     * @return  mixed  DB_OK on success, NULL on no more rows or
175
     *                 a DB_Error object on error
176
     *
177
     * @access public
178
     */
179
 
180
    function fetchInto(&$ar, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
181
    {
182
        $this->getRows();
183
        if ($this->_record !== null) $this->_record = next($this->_recordset);
184
        else $this->_record = current($this->_recordset);
185
        $ar = $this->_record;
186
        if (!$ar) {
187
            return null;
188
        }
189
        return DB_OK;
190
    }
191
 
192
    /**
193
     * return all records
194
     *
195
     * returns a hash of all records, basically returning
196
     * a copy of $this->_recordset
197
     * @param  integer   $fetchmode  format of fetched row
198
     * @param  integer   $rownum     the row number to fetch (not used, here for interface compatibility)
199
     *
200
     * @return  mixed  DB_OK on success, NULL on no more rows or
201
     *                 a DB_Error object on error
202
     *
203
     * @access public
204
     */
205
    function fetchAll($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
206
    {
207
        $this->getRows();
208
        return($this->_recordset);
209
    }
210
 
211
    /**
212
     * Get the the number of columns in a result set.
213
     *
214
     * @return int the number of columns, or a DB error
215
     *
216
     * @access public
217
     */
218
    function numCols($result)
219
    {
220
        $this->getRows();
221
        return(count(array_keys($this->_record)));
222
    }
223
 
224
    function cmp($a, $b)
225
    {
226
        return(strcmp(strtolower($this->_recordset[$a][$this->dbh->sorting]), strtolower($this->_recordset[$b][$this->dbh->sorting])));
227
    }
228
 
229
    /**
230
     * Get the number of rows in a result set.
231
     *
232
     * @return int the number of rows, or a DB error
233
     *
234
     * @access public
235
     */
236
    function numRows()
237
    {
238
        $this->getRows();
239
        return $this->row_counter;
240
    }
241
 
242
    /**
243
     * Get the next result if a batch of queries was executed.
244
     *
245
     * @return bool true if a new result is available or false if not.
246
     *
247
     * @access public
248
     */
249
    function nextResult()
250
    {
251
        return $this->dbh->nextResult($this->result);
252
    }
253
 
254
    /**
255
     * Frees the resources allocated for this result set.
256
     * @return  int     error code
257
     *
258
     * @access public
259
     */
260
    function free()
261
    {
262
        $this->_recordset = null;
263
        $this->_record = null;
264
        ldap_free_result($this->result);
265
        $this->result = null;
266
        return true;
267
    }
268
 
269
    /**
270
    * @deprecated
271
    */
272
    function tableInfo($mode = null)
273
    {
274
        return $this->dbh->tableInfo($this->result, $mode);
275
    }
276
 
277
    /**
278
    * returns the actual rows number
279
    * @return integer
280
    */
281
    function getRowCounter()
282
    {
283
        $this->getRows();
284
        return $this->row_counter;
285
    }
286
}
287
 
288
/**
289
 * LDAP DB interface class
290
 *
291
 * LDAP extends DB_common to provide DB compliant
292
 * access to LDAP servers
293
 *
294
 * @version 1.0
295
 * @author Ludovico Magnocavallo <ludo@sumatrasolutions.com>
296
 * @package DB
297
 */
298
 
299
class DB_ldap extends DB_common
300
{
301
    // {{{ properties
302
 
303
    /**
304
     * LDAP connection
305
     * @access private
306
     */
307
    var $connection;
308
    /**
309
     * base dn
310
     * @access private
311
     */
312
    var $base           = '';
313
    /**
314
     * default base dn
315
     * @access private
316
     */
317
    var $d_base           = '';
318
    /**
319
     * query base dn
320
     * @access private
321
     */
322
    var $q_base           = '';
323
    /**
324
     * array of LDAP actions that only manipulate data
325
     * returning a true/false value
326
     * @access private
327
     */
328
    var $manip          = array('add', 'compare', 'delete', 'modify', 'mod_add', 'mod_del', 'mod_replace', 'rename');
329
    /**
330
     * store the default real LDAP action to perform
331
     * @access private
332
     */
333
    var $action         = 'search';
334
    /**
335
     * store the real LDAP action to perform
336
     * (ie PHP ldap function to call) for a query
337
     * @access private
338
     */
339
    var $q_action       = '';
340
    /**
341
     * store optional parameters passed
342
     *  to the real LDAP action
343
     * @access private
344
     */
345
    var $q_params       = array();
346
 
347
    // }}}
348
 
349
    /**
350
     * Constructor, calls DB_common constructor
351
     *
352
     * @see DB_common::DB_common()
353
     */
354
    function DB_ldap()
355
    {
356
        $this->DB_common();
357
        $this->phptype = 'ldap';
358
        $this->dbsyntax = 'ldap';
359
        $this->features = array(
360
            'prepare'       => false,
361
            'pconnect'      => false,
362
            'transactions'  => false,
363
            'limit'         => false
364
        );
365
        $this->errorcode_map = array(
366
            0x10 => DB_ERROR_NOSUCHFIELD,               // LDAP_NO_SUCH_ATTRIBUTE
367
            0x11 => DB_ERROR_INVALID,                   // LDAP_UNDEFINED_TYPE
368
            0x12 => DB_ERROR_INVALID,                   // LDAP_INAPPROPRIATE_MATCHING
369
            0x13 => DB_ERROR_INVALID,                   // LDAP_CONSTRAINT_VIOLATION
370
            0x14 => DB_ERROR_ALREADY_EXISTS,            // LDAP_TYPE_OR_VALUE_EXISTS
371
            0x15 => DB_ERROR_INVALID,                   // LDAP_INVALID_SYNTAX
372
            0x20 => DB_ERROR_NOT_FOUND,                 // LDAP_NO_SUCH_OBJECT
373
            0x21 => DB_ERROR_NOT_FOUND,                 // LDAP_ALIAS_PROBLEM
374
            0x22 => DB_ERROR_INVALID,                   // LDAP_INVALID_DN_SYNTAX
375
            0x23 => DB_ERROR_INVALID,                   // LDAP_IS_LEAF
376
            0x24 => DB_ERROR_INVALID,                   // LDAP_ALIAS_DEREF_PROBLEM
377
            0x30 => DB_ERROR_ACCESS_VIOLATION,          // LDAP_INAPPROPRIATE_AUTH
378
            0x31 => DB_ERROR_ACCESS_VIOLATION,          // LDAP_INVALID_CREDENTIALS
379
            0x32 => DB_ERROR_ACCESS_VIOLATION,          // LDAP_INSUFFICIENT_ACCESS
380
            0x40 => DB_ERROR_MISMATCH,                  // LDAP_NAMING_VIOLATION
381
            0x41 => DB_ERROR_MISMATCH,                  // LDAP_OBJECT_CLASS_VIOLATION
382
            0x44 => DB_ERROR_ALREADY_EXISTS,            // LDAP_ALREADY_EXISTS
383
            0x51 => DB_ERROR_CONNECT_FAILED,            // LDAP_SERVER_DOWN
384
            0x57 => DB_ERROR_SYNTAX                     // LDAP_FILTER_ERROR
385
        );
386
    }
387
 
388
    /**
389
     * Connect and bind to LDAP server with either anonymous or authenticated bind depending on dsn info
390
     *
391
     * @param array $dsninfo dsn info as passed by DB::connect()
392
     * @param boolean $persistent kept for interface compatibility
393
     * @return DB_OK if successfully connected. A DB error code is returned on failure.
394
     */
395
    function connect($dsninfo, $persistent = false)
396
    {
397
        if (!PEAR::loadExtension('ldap'))
398
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
399
 
400
        $this->dsn = $dsninfo;
401
        $user   = $dsninfo['username'];
402
        $pw     = $dsninfo['password'];
403
        $host 	= $dsninfo['hostspec'];
404
        $port 	= $dsninfo['port'];
405
        $this->base = $dsninfo['database'];
406
        $this->d_base = $this->base;
407
 
408
        if (empty($host)) {
409
            return $this->raiseError("no host specified $host");
410
        } // else ...
411
 
412
        if (isset($port)) {
413
            $conn = ldap_connect($host, $port);
414
        } else {
415
            $conn = ldap_connect($host);
416
        }
417
        if (!$conn) {
418
            return $this->raiseError(DB_ERROR_CONNECT_FAILED);
419
        }
420
        if ($user && $pw) {
421
            $bind = @ldap_bind($conn, $user, $pw);
422
        } else {
423
            $bind = @ldap_bind($conn);
424
        }
425
        if (!$bind) {
426
            return $this->raiseError(DB_ERROR_BIND_FAILED);
427
        }
428
        $this->connection = $conn;
429
        return DB_OK;
430
    }
431
 
432
    /**
433
     * Unbinds from LDAP server
434
     *
435
     * @return int ldap_unbind() return value
436
     */
437
    function disconnect()
438
    {
439
        $ret = @ldap_unbind($this->connection);
440
        $this->connection = null;
441
        return $ret;
442
    }
443
 
444
 
445
    /**
446
     * Performs a request against the LDAP server
447
     *
448
     * The type of request (and the corresponding PHP ldap function called)
449
     * depend on two additional parameters, added in respect to the
450
     * DB_common interface.
451
     *
452
     * @param string $filter text of the request to send to the LDAP server
453
     * @param string $action type of request to perform, defaults to search (ldap_search())
454
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
455
     * @return result from ldap function or DB Error object if no result
456
     */
457
    function simpleQuery($filter, $action = null, $params = null)
458
    {
459
        if ($action === null) {
460
            $action = (!empty($this->q_action) ? $this->q_action : $this->action);
461
        }
462
        if ($params === null) {
463
            $params = (count($this->q_params) > 0 ? $this->q_params : array());
464
        }
465
        if (!$this->isManip($action)) {
466
            $base = $this->q_base ? $this->q_base : $this->base;
467
            $attributes = array();
468
            $attrsonly = 0;
469
            $sizelimit = 0;
470
            $timelimit = 0;
471
            $deref = LDAP_DEREF_NEVER;
472
            $sorting = '';
473
            $sorting_method = '';
474
            reset($params);
475
            while (list($k, $v) = each($params)) {
476
                if (isset(${$k})) ${$k} = $v;
477
            }
478
            $this->sorting = $sorting;
479
            $this->sorting_method = $sorting_method;
480
            $this->attributes = $attributes;
481
            # double escape char for filter: '(o=Przedsi\C4\99biorstwo)' => '(o=Przedsi\\C4\\99biorstwo)'
482
            $filter = str_replace('\\', '\\\\', $filter);
483
            $this->last_query = $filter;
484
            if ($action == 'search')
485
                $result = @ldap_search($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref);
486
            else if ($action == 'list')
487
                $result = @ldap_list($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref);
488
            else if ($action == 'read')
489
                $result = @ldap_read($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref);
490
            else
491
                return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION);
492
            if (!$result) {
493
                return $this->ldapRaiseError();
494
            }
495
        } else {
496
            # If first argument is an array, it contains the entry with DN.
497
            if (is_array($filter)) {
498
                $entry = $filter;
499
                $filter = $entry["dn"];
500
            } else {
501
                $entry = array();
502
            }
503
            unset($entry["dn"]);
504
            $attribute      = '';
505
            $value          = '';
506
            $newrdn         = '';
507
            $newparent      = '';
508
            $deleteoldrdn   = false;
509
            reset($params);
510
            while (list($k, $v) = each($params)) {
511
                if (isset(${$k})) ${$k} = $v;
512
            }
513
            $this->last_query = $filter;
514
            if ($action == 'add')
515
                $result = @ldap_add($this->connection, $filter, $entry);
516
            else if ($action == 'compare')
517
                $result = @ldap_add($this->connection, $filter, $attribute, $value);
518
            else if ($action == 'delete')
519
                $result = @ldap_delete($this->connection, $filter);
520
            else if ($action == 'modify')
521
                $result = @ldap_modify($this->connection, $filter, $entry);
522
            else if ($action == 'mod_add')
523
                $result = @ldap_mod_add($this->connection, $filter, $entry);
524
            else if ($action == 'mod_del')
525
                $result = @ldap_mod_del($this->connection, $filter, $entry);
526
            else if ($action == 'mod_replace')
527
                $result = @ldap_mod_replace($this->connection, $filter, $entry);
528
            else if ($action == 'rename')
529
                $result = @ldap_rename($this->connection, $filter, $newrdn, $newparent, $deleteoldrdn);
530
            else
531
                return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION);
532
            if (!$result) {
533
                return $this->ldapRaiseError();
534
            }
535
        }
536
        $this->freeQuery();
537
        return $result;
538
    }
539
 
540
    /**
541
     * Executes a query performing variables substitution in the query text
542
     *
543
     * @param string $stmt text of the request to send to the LDAP server
544
     * @param array $data query variables values to substitute
545
     * @param string $action type of request to perform, defaults to search (ldap_search())
546
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
547
     * @return LDAP_result object or DB Error object if no result
548
     * @see DB_common::executeEmulateQuery $this->simpleQuery()
549
     */
550
    function execute($stmt, $data = false, $action = null, $params = array())
551
    {
552
        $this->q_params = $params;
553
        $realquery = $this->executeEmulateQuery($stmt, $data);
554
        if (DB::isError($realquery)) {
555
            return $realquery;
556
        }
557
        $result = $this->simpleQuery($realquery);
558
        if (DB::isError($result) || $result === DB_OK) {
559
            return $result;
560
        } else {
561
            return new LDAP_result($this, $result);
562
        }
563
    }
564
 
565
    /**
566
     * Executes multiple queries performing variables substitution for each query
567
     *
568
     * @param string $stmt text of the request to send to the LDAP server
569
     * @param array $data query variables values to substitute
570
     * @param string $action type of request to perform, defaults to search (ldap_search())
571
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
572
     * @return LDAP_result object or DB Error object if no result
573
     * @see DB_common::executeMultiple
574
     */
575
    function executeMultiple($stmt, &$data, $action = null, $params = array())
576
    {
577
        $this->q_action = $action ? $action : $this->action;
578
        $this->q_params = $params;
579
        return(parent::executeMultiple($stmt, $data));
580
    }
581
 
582
    /**
583
     * Executes a query substituting variables if any are present
584
     *
585
     * @param string $query text of the request to send to the LDAP server
586
     * @param array $data query variables values to substitute
587
     * @param string $action type of request to perform, defaults to search (ldap_search())
588
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
589
     * @return LDAP_result object or DB Error object if no result
590
     * @see DB_common::prepare() $this->execute()$this->simpleQuery()
591
     */
592
    function &query($query, $data = array(), $action = null, $params = array()) {
593
        // $this->q_action = $action ? $action : $this->action;
594
        // $this->q_params = $params;
595
        if (sizeof($data) > 0) {
596
            $sth = $this->prepare($query);
597
            if (DB::isError($sth)) {
598
                return $sth;
599
            }
600
            return $this->execute($sth, $data);
601
        } else {
602
            $result = $this->simpleQuery($query);
603
            if (DB::isError($result) || $result === DB_OK) {
604
                return $result;
605
            } else {
606
                return new LDAP_result($this, $result);
607
            }
608
        }
609
    }
610
 
611
    /**
612
     * Modifies a query to return only a set of rows, stores $from and $count for LDAP_result
613
     *
614
     * @param string $query text of the request to send to the LDAP server
615
     * @param int $from record position from which to start returning data
616
     * @param int $count number of records to return
617
     * @return modified query text (no modifications are made, see above)
618
     */
619
    function modifyLimitQuery($query, $from, $count)
620
    {
621
        $this->limit_from = $from;
622
        $this->limit_count = $count;
623
        return $query;
624
    }
625
 
626
    /**
627
     * Executes a query returning only a specified number of rows
628
     *
629
     * This method only saves the $from and $count parameters for LDAP_result
630
     * where the actual records processing takes place
631
     *
632
     * @param string $query text of the request to send to the LDAP server
633
     * @param int $from record position from which to start returning data
634
     * @param int $count number of records to return
635
     * @param string $action type of request to perform, defaults to search (ldap_search())
636
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
637
     * @return LDAP_result object or DB Error object if no result
638
     */
639
    function limitQuery($query, $from, $count, $action = null, $params = array())
640
    {
641
        $query = $this->modifyLimitQuery($query, $from, $count);
642
        $this->q_action = $action ? $action : $this->action;
643
        $this->q_params = $params;
644
        return $this->query($query, $action, $params);
645
    }
646
 
647
    /**
648
     * Fetch the first column of the first row of data returned from
649
     * a query.  Takes care of doing the query and freeing the results
650
     * when finished.
651
     *
652
     * @param $query the SQL query
653
     * @param $data if supplied, prepare/execute will be used
654
     *        with this array as execute parameters
655
     * @param string $action type of request to perform, defaults to search (ldap_search())
656
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
657
     * @return array
658
     * @see DB_common::getOne()
659
     * @access public
660
     */
661
    function &getOne($query, $data = array(), $action = null, $params = array())
662
    {
663
        $this->q_action = $action ? $action : $this->action;
664
        $this->q_params = $params;
665
        return(parent::getOne($query, $data));
666
    }
667
 
668
    /**
669
     * Fetch the first row of data returned from a query.  Takes care
670
     * of doing the query and freeing the results when finished.
671
     *
672
     * @param $query the SQL query
673
     * @param $fetchmode the fetch mode to use
674
     * @param $data array if supplied, prepare/execute will be used
675
     *        with this array as execute parameters
676
     * @param string $action type of request to perform, defaults to search (ldap_search())
677
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
678
     * @access public
679
     * @return array the first row of results as an array indexed from
680
     * 0, or a DB error code.
681
     * @see DB_common::getRow()
682
     * @access public
683
     */
684
    function &getRow($query,
685
                     $data = null,
686
                     $fetchmode = DB_FETCHMODE_DEFAULT,
687
                     $action = null, $params = array())
688
    {
689
        $this->q_action = $action ? $action : $this->action;
690
        $this->q_params = $params;
691
        return(parent::getRow($query, $data, $fetchmode));
692
    }
693
 
694
    /**
695
     * Fetch the first column of data returned from a query.  Takes care
696
     * of doing the query and freeing the results when finished.
697
     *
698
     * @param $query the SQL query
699
     * @param $col which column to return (integer [column number,
700
     * starting at 0] or string [column name])
701
     * @param $data array if supplied, prepare/execute will be used
702
     *        with this array as execute parameters
703
     * @param string $action type of request to perform, defaults to search (ldap_search())
704
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
705
     * @access public
706
     * @return array an indexed array with the data from the first
707
     * row at index 0, or a DB error code.
708
     * @see DB_common::getCol()
709
     * @access public
710
     */
711
    function &getCol($query, $col = 0, $data = array(), $action = null, $params = array())
712
    {
713
        $this->q_action = $action ? $action : $this->action;
714
        $this->q_params = $params;
715
        return(parent::getCol($query, $col, $data));
716
    }
717
 
718
    /**
719
     * Calls DB_common::getAssoc()
720
     *
721
     * @param $query the SQL query
722
     * @param $force_array (optional) used only when the query returns
723
     * exactly two columns.  If true, the values of the returned array
724
     * will be one-element arrays instead of scalars.
725
     * starting at 0] or string [column name])
726
     * @param array $data if supplied, prepare/execute will be used
727
     *        with this array as execute parameters
728
     * @param $fetchmode the fetch mode to use
729
     * @param boolean $group see DB_Common::getAssoc()
730
     * @param string $action type of request to perform, defaults to search (ldap_search())
731
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
732
     * @access public
733
     * @return array an indexed array with the data from the first
734
     * row at index 0, or a DB error code.
735
     * @see DB_common::getAssoc()
736
     * @access public
737
     */
738
    function &getAssoc($query, $force_array = false, $data = array(),
739
                       $fetchmode = DB_FETCHMODE_ORDERED, $group = false,
740
                       $action = null, $params = array())
741
    {
742
        $this->q_action = $action ? $action : $this->action;
743
        $this->q_params = $params;
744
        return(parent::getAssoc($query, $force_array, $data, $fetchmode, $group));
745
    }
746
 
747
    /**
748
     * Fetch all the rows returned from a query.
749
     *
750
     * @param $query the SQL query
751
     * @param array $data if supplied, prepare/execute will be used
752
     *        with this array as execute parameters
753
     * @param $fetchmode the fetch mode to use
754
     * @param string $action type of request to perform, defaults to search (ldap_search())
755
     * @param array $params array of additional parameters to pass to the PHP ldap function requested
756
     * @access public
757
     * @return array an nested array, or a DB error
758
     * @see DB_common::getAll()
759
     */
760
    function &getAll($query,
761
                     $data = null,
762
                     $fetchmode = DB_FETCHMODE_DEFAULT,
763
                     $action = null, $params = array())
764
    {
765
        $this->q_action = $action ? $action : $this->action;
766
        $this->q_params = $params;
767
        return(parent::getAll($query, $data, $fetchmode));
768
    }
769
 
770
    function numRows($result)
771
    {
772
        return $result->numRows();
773
    }
774
 
775
    function getTables()
776
    {
777
        return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE);
778
    }
779
 
780
    function getListOf($type)
781
    {
782
        return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE);
783
    }
784
 
785
    function isManip($action)
786
    {
787
        return(in_array($action, $this->manip));
788
    }
789
 
790
    function freeResult()
791
    {
792
        return true;
793
    }
794
 
795
    function freeQuery($query = '')
796
    {
797
        $this->q_action = '';
798
        $this->q_base   = '';
799
        $this->q_params = array();
800
        $this->attributes = null;
801
        $this->sorting = '';
802
        return true;
803
    }
804
 
805
    // Deprecated, will be removed in future releases.
806
    function base($base = null)
807
    {
808
        $this->q_base = ($base !== null) ? $base : null;
809
        return true;
810
    }
811
 
812
    function ldapSetBase($base = null)
813
    {
814
        $this->base = ($base !== null) ? $base : $this->d_base;
815
        $this->q_base = '';
816
        return true;
817
    }
818
 
819
    function ldapSetAction($action = 'search')
820
    {
821
        if ($action != 'search' && $action != 'list' && $action != 'read') {
822
            return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION);
823
        }
824
        $this->action = $action;
825
        $this->q_action = '';
826
        return true;
827
    }
828
 
829
    /**
830
     * Get the next value in a sequence.
831
     *
832
     * LDAP provides transactions for only one entry and we need to
833
     * prevent race condition. If unique value before and after modify
834
     * aren't equal then wait and try again.
835
     *
836
     * The name of sequence is LDAP DN of entry.
837
     *
838
     * @access public
839
     * @param string $seq_name the DN of the sequence
840
     * @param bool $ondemand whether to create the sequence on demand
841
     * @return a sequence integer, or a DB error
842
     */
843
    function nextId($seq_name, $ondemand = true)
844
    {
845
        $repeat = 0;
846
        do {
847
            // Get the sequence entry
848
            $this->base($seq_name);
849
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
850
            $data = $this->getRow("objectClass=*");
851
            $this->popErrorHandling();
852
 
853
            if (DB::isError($data)) {
854
                // DB_ldap doesn't use DB_ERROR_NOT_FOUND
855
                if ($ondemand && $repeat == 0
856
                && $data->getCode() == DB_ERROR) {
857
                // Try to create sequence and repeat
858
                    $repeat = 1;
859
                    $data = $this->createSequence($seq_name);
860
                    if (DB::isError($data)) {
861
                        return $this->ldapRaiseError($data);
862
                    }
863
                } else {
864
                    // Other error
865
                    return $this->ldapRaiseError($data);
866
                }
867
            } else {
868
                // Increment sequence value
869
                $data["cn"]++;
870
                // Unique identificator of transaction
871
                $seq_unique = mt_rand();
872
                $data["uid"] = $seq_unique;
873
                // Modify the LDAP entry
874
                $this->pushErrorHandling(PEAR_ERROR_RETURN);
875
                $data = $this->simpleQuery($data, 'modify');
876
                $this->popErrorHandling();
877
                if (DB::isError($data)) {
878
                    return $this->ldapRaiseError($data);
879
                }
880
                // Get the entry and check if it contains our unique value
881
                $this->base($seq_name);
882
                $data = $this->getRow("objectClass=*");
883
                if (DB::isError($data)) {
884
                    return $this->ldapRaiseError($data);
885
                }
886
                if ($data["uid"] != $seq_unique) {
887
                    // It is not our entry. Wait a little time and repeat
888
                    sleep(1);
889
                    $repeat = 1;
890
                } else {
891
                    $repeat = 0;
892
                }
893
            }
894
        } while ($repeat);
895
 
896
        if (DB::isError($data)) {
897
            return $data;
898
        }
899
        return $data["cn"];
900
    }
901
 
902
    /**
903
     * Create the sequence
904
     *
905
     * The sequence entry is based on core schema with extensibleObject,
906
     * so it should work with any LDAP server which doesn't check schema
907
     * or supports extensibleObject object class.
908
     *
909
     * Sequence name have to be DN started with "sn=$seq_id,", i.e.:
910
     *
911
     * $seq_name = "sn=uidNumber,ou=sequences,dc=php,dc=net";
912
     *
913
     * dn: $seq_name
914
     * objectClass: top
915
     * objectClass: extensibleObject
916
     * sn: $seq_id
917
     * cn: $seq_value
918
     * uid: $seq_uniq
919
     *
920
     * @param string $seq_name the DN of the sequence
921
     * @return mixed DB_OK on success or DB error on error
922
     * @access public
923
     */
924
    function createSequence($seq_name)
925
    {
926
        // Extract $seq_id from DN
927
        ereg("^([^,]*),", $seq_name, $regs);
928
        $seq_id = $regs[1];
929
 
930
        // Create the sequence entry
931
        $data = array(
932
            dn => $seq_name,
933
            objectclass => array("top", "extensibleObject"),
934
            sn => $seq_id,
935
            cn => 0,
936
            uid => 0
937
        );
938
 
939
        // Add the LDAP entry
940
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
941
        $data = $this->simpleQuery($data, 'add');
942
        $this->popErrorHandling();
943
        return $data;
944
    }
945
 
946
    /**
947
     * Drop a sequence
948
     *
949
     * @param string $seq_name the DN of the sequence
950
     * @return mixed DB_OK on success or DB error on error
951
     * @access public
952
     */
953
    function dropSequence($seq_name)
954
    {
955
        // Delete the sequence entry
956
        $data = array(
957
            dn => $seq_name,
958
        );
959
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
960
        $data = $this->simpleQuery($data, 'delete');
961
        $this->popErrorHandling();
962
        return $data;
963
    }
964
 
965
    // {{{ ldapRaiseError()
966
 
967
    function ldapRaiseError($errno = null)
968
    {
969
        if ($errno === null) {
970
            $errno = $this->errorCode(ldap_errno($this->connection));
971
        }
972
        if ($this->q_action !== null) {
973
            return $this->raiseError($errno, null, null,
974
                sprintf('%s base="%s" filter="%s"',
975
                    $this->q_action, $this->q_base, $this->last_query
976
                ),
977
                $errno == DB_ERROR_UNKNOWN_LDAP_ACTION ? null : @ldap_error($this->connection));
978
        } else {
979
            return $this->raiseError($errno, null, null, "???",
980
                @ldap_error($this->connection));
981
        }
982
    }
983
 
984
    // }}}
985
 
986
}
987
 
988
/*
989
 * Local variables:
990
 * tab-width: 4
991
 * c-basic-offset: 4
992
 * End:
993
 */
994
?>