home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / ldap.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  30.2 KB  |  912 lines

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