home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / DB / ldap.php < prev    next >
Encoding:
PHP Script  |  2005-07-07  |  33.8 KB  |  995 lines

  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.22 2005/06/16 19:17:54 ludoo 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. ?>
  995.