Net_LDAP
[ class tree: Net_LDAP ] [ index: Net_LDAP ] [ all elements ]

Source for file LDIF.php

Documentation is available at LDIF.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3.  
  4. require_once 'PEAR.php';
  5. require_once 'Net/LDAP.php';
  6. require_once 'Net/LDAP/Entry.php';
  7. require_once 'Net/LDAP/Util.php';
  8.  
  9. /**
  10. * LDIF capabilitys for Net_LDAP, closely taken from PERLs Net::LDAP
  11. *
  12. * It provides a means to convert between Net_LDAP_Entry objects and LDAP entries
  13. * represented in LDIF format files. Reading and writing are supported and may
  14. * manipulate single entries or lists of entries.
  15. *
  16. * Usage example:
  17. * <code>
  18. * // Read and parse an ldif-file into Net_LDAP_Entry objects
  19. * // and print out the DNs. Store the entries for later use.
  20. * require 'Net/LDAP/LDIF.php';
  21. * $options = array(
  22. *       'onerror' => 'die'
  23. * );
  24. * $entries = array();
  25. * $ldif = new Net_LDAP_LDIF('test.ldif', 'r', $options);
  26. * do {
  27. *       $entry = $ldif->read_entry();
  28. *       $dn    = $entry->dn();
  29. *       echo " done building entry: $dn\n";
  30. *       array_push($entries, $entry);
  31. * } while (!$ldif->eof());
  32. * $ldif->done();
  33. *
  34. *
  35. * // write those entries to another file
  36. * $ldif = new Net_LDAP_LDIF('test.out.ldif', 'w', $options);
  37. * $ldif->write_entry($entries);
  38. * $ldif->done();
  39. * </code>
  40. *
  41. @category Net
  42. @package  Net_LDAP
  43. @author   Benedikt Hallinger <beni@php.net>
  44. @license  http://www.gnu.org/copyleft/lesser.html LGPL
  45. @version  CVS: $Id: LDIF.php,v 1.22 2008/01/11 08:18:42 beni Exp $
  46. @link     http://pear.php.net/package/Net_LDAP/
  47. @see      http://www.ietf.org/rfc/rfc2849.txt
  48. @todo     Error handling should be PEARified
  49. @todo     LDAPv3 controls are not implemented yet
  50. */
  51. class Net_LDAP_LDIF extends PEAR
  52. {
  53.     /**
  54.     * Options
  55.     *
  56.     * @access private
  57.     * @var array 
  58.     */
  59.     var $_options = array(
  60.         'encode'    => 'base64',
  61.         'onerror'   => 'undef',
  62.         'change'    => 0,
  63.         'lowercase' => 0,
  64.         'sort'      => 0,
  65.         'version'   => 1,
  66.         'wrap'      => 78,
  67.         'raw'       => ''
  68.     );
  69.  
  70.     /**
  71.     * Errorcache
  72.     *
  73.     * @access private
  74.     * @var array 
  75.     */
  76.     var $_error = array(
  77.         'error' => null,
  78.         'line'  => 0
  79.     );
  80.  
  81.     /**
  82.     * Filehandle for read/write
  83.     *
  84.     * @access private
  85.     * @var array 
  86.     */
  87.     var $_FH = null;
  88.  
  89.     /**
  90.     * Says, if we opened the filehandle ourselves
  91.     *
  92.     * @access private
  93.     * @var array 
  94.     */
  95.     var $_FH_opened = false;
  96.  
  97.     /**
  98.     * Linecounter for input file handle
  99.     *
  100.     * @access private
  101.     * @var array 
  102.     */
  103.     var $_input_line = 0;
  104.  
  105.     /**
  106.     * counter for processed entries
  107.     *
  108.     * @access private
  109.     * @var int 
  110.     */
  111.     var $_entrynum = 0;
  112.  
  113.     /**
  114.     * Mode we are working in
  115.     *
  116.     * Either 'r', 'a' or 'w'
  117.     *
  118.     * @access private
  119.     * @var string 
  120.     */
  121.     var $_mode = false;
  122.  
  123.     /**
  124.     * Tells, if the LDIF version string was already written
  125.     *
  126.     * @access private
  127.     * @var boolean 
  128.     */
  129.     var $_version_written = false;
  130.  
  131.     /**
  132.     * Cache for lines that have build the current entry
  133.     *
  134.     * @access private
  135.     * @var boolean 
  136.     */
  137.     var $_lines_cur = array();
  138.  
  139.     /**
  140.     * Cache for lines that will build the next entry
  141.     *
  142.     * @access private
  143.     * @var boolean 
  144.     */
  145.     var $_lines_next = array();
  146.  
  147.     /**
  148.     * Open LDIF file for reading or for writing
  149.     *
  150.     * new (FILE):
  151.     * Open the file read-only. FILE may be the name of a file
  152.     * or an already open filehandle.
  153.     * If the file doesn't exist, it will be created if in write mode.
  154.     *
  155.     * new (FILE, MODE, OPTIONS):
  156.     *     Open the file with the given MODE (see PHPs fopen()), eg "w" or "a".
  157.     *     FILE may be the name of a file or an already open filehandle.
  158.     *     PERLs Net_LDAP "FILE|" mode does not work curently.
  159.     *
  160.     *     OPTIONS is an associative array and may contain:
  161.     *       encode => 'none' | 'canonical' | 'base64'
  162.     *         Some DN values in LDIF cannot be written verbatim and have to be encoded in some way:
  163.     *         'none'       No encoding.
  164.     *         'canonical'  See "canonical_dn()" in Net::LDAP::Util.
  165.     *         'base64'     Use base64. (default, this differs from the Perl interface.
  166.     *                                   The perl default is "none"!)
  167.     *
  168.     *       onerror => 'die' | 'warn' | undef
  169.     *         Specify what happens when an error is detected.
  170.     *         'die'  Net_LDAP_LDIF will croak with an appropriate message.
  171.     *         'warn' Net_LDAP_LDIF will warn (echo) with an appropriate message.
  172.     *         undef  Net_LDAP_LDIF will not warn (default), use error().
  173.     *
  174.     *       change => 1
  175.     *         Write entry changes to the LDIF file instead of the entries itself. I.e. write LDAP
  176.     *         operations acting on the entries to the file instead of the entries contents.
  177.     *         This writes the changes usually carried out by an update() to the LDIF file.
  178.     *
  179.     *       lowercase => 1
  180.     *         Convert attribute names to lowercase when writing.
  181.     *
  182.     *       sort => 1
  183.     *         Sort attribute names when writing entries according to the rule:
  184.     *         objectclass first then all other attributes alphabetically sorted
  185.     *
  186.     *       version => '1'
  187.     *         Set the LDIF version to write to the resulting LDIF file.
  188.     *         According to RFC 2849 currently the only legal value for this option is 1.
  189.     *         When this option is set Net_LDAP_LDIF tries to adhere more strictly to
  190.     *         the LDIF specification in RFC2489 in a few places.
  191.     *         The default is undef meaning no version information is written to the LDIF file.
  192.     *
  193.     *       wrap => 78
  194.     *         Number of columns where output line wrapping shall occur.
  195.     *         Default is 78. Setting it to 40 or lower inhibits wrapping.
  196.     *
  197.     *       [NOT IMPLEMENTED] raw => REGEX
  198.     *         Use REGEX to denote the names of attributes that are to be
  199.     *         considered binary in search results.
  200.     *         Example: raw => "/(?i:^jpegPhoto|;binary)/i"
  201.     *         Note: if the entry has a valid LDAP connection, then binary checks
  202.     *               are also done through the schema.
  203.     *
  204.     * @param string|ressource$file    Filename or filehandle
  205.     * @param string           $mode    Mode to open filename
  206.     * @param array            $options Options like described above
  207.     */
  208.     function Net_LDAP_LDIF($file$mode 'r'$options = array()) {
  209.         $this->PEAR('Net_LDAP_Error')// default error class
  210.  
  211.         // First, parse options
  212.         // todo: maybe implement further checks on possible values
  213.         foreach ($options as $option => $value{
  214.             if (!array_key_exists($option$this->_options)) {
  215.                 $this->_dropError('Net_LDAP_LDIF error: option '.$option.' not known!');
  216.                 return;
  217.             else {
  218.                 $this->_options[$optionstrtolower($value);
  219.             }
  220.         }
  221.  
  222.         // setup LDIF class
  223.         $this->version($this->_options['version']);
  224.  
  225.         // setup file mode
  226.         if (!preg_match('/^[rwa]\+?$/'$mode)) {
  227.             $this->_dropError('Net_LDAP_LDIF error: file mode '.$mode.' not supported!');
  228.         else {
  229.             $this->_mode $mode;
  230.  
  231.             // setup filehandle
  232.             if (is_resource($file)) {
  233.                 // TODO: checks on mode possible?
  234.                 $this->_FH =$file;
  235.             else {
  236.                 $imode substr($this->_mode01);
  237.                 if ($imode == 'r'{
  238.                     if (!file_exists($file)) {
  239.                         $this->_dropError('Unable to open '.$file.' for read: file not found');
  240.                         $this->_mode = false;
  241.                     }
  242.                     if (!is_readable($file)) {
  243.                         $this->_dropError('Unable to open '.$file.' for read: permission denied');
  244.                         $this->_mode = false;
  245.                     }
  246.                 }
  247.  
  248.                 if (($imode == 'w' || $imode == 'a')) {
  249.                     if (file_exists($file)) {
  250.                         if (!is_writable($file)) {
  251.                             $this->_dropError('Unable to open '.$file.' for write: permission denied');
  252.                             $this->_mode = false;
  253.                         }
  254.                     else {
  255.                         if (!@touch($file)) {
  256.                             $this->_dropError('Unable to create '.$file.' for write: permission denied');
  257.                             $this->_mode = false;
  258.                         }
  259.                     }
  260.                 }
  261.  
  262.                 if ($this->_mode{
  263.                     $this->_FH @fopen($file$this->_mode);
  264.                     if (false === $this->_FH{
  265.                         // Fallback; should never be reached if tests above are good enough!
  266.                         $this->_dropError('Net_LDAP_LDIF error: Could not open file '.$file);
  267.                     else {
  268.                         $this->_FH_opened = true;
  269.                     }
  270.                 }
  271.             }
  272.         }
  273.     }
  274.  
  275.     /**
  276.     * Read one entry from the file and return it as a Net::LDAP::Entry object.
  277.     *
  278.     * @return Net_LDAP_Entry 
  279.     */
  280.     function read_entry({
  281.         // read fresh lines, set them as current lines and create the entry
  282.         $attrs $this->next_lines(true);
  283.         if (count($attrs> 0{
  284.             $this->_lines_cur $attrs;
  285.         }
  286.         return $this->current_entry();
  287.     }
  288.  
  289.     /**
  290.     * Returns true when the end of the file is reached.
  291.     */
  292.     function eof({
  293.         return feof($this->_FH);
  294.     }
  295.  
  296.     /**
  297.     * Write the entry or entries to the LDIF file.
  298.     *
  299.     * If you want to build an LDIF file containing several entries AND
  300.     * you want to call write_entry() several times, you must open the filehandle
  301.     * in append mode ("a"), otherwise you will always get the last entry only.
  302.     *
  303.     * @param Net_LDAP_Entry|arrayEntry or array of entries
  304.     * @todo impement the options 'raw' with auto-schema check
  305.     * @todo implement operations on whole entries (adding and moving a whole entry)
  306.     */
  307.     function write_entry($entries{
  308.         if (!is_array($entries)) {
  309.             $entries = array($entries);
  310.         }
  311.  
  312.         foreach ($entries as $entry{
  313.             $this->_entrynum++;
  314.             if (!is_a($entry'Net_LDAP_Entry')) {
  315.                 $this->_dropError('Net_LDAP_LDIF error: entry '.$this->_entrynum.' is not an Net_LDAP_Entry object');
  316.             else {
  317.                 if ($this->_options['change']{
  318.                     // LDIF change mode
  319.                     // fetch change information from entry
  320.                     $entry_attrs_changes $entry->getChanges();
  321.                     $num_of_changes      count($entry_attrs_changes['add'])
  322.                                            + count($entry_attrs_changes['replace'])
  323.                                            + count($entry_attrs_changes['delete']);
  324.  
  325.                     $is_changed ($num_of_changes > 0 || $entry->willBeDeleted());
  326.  
  327.                     // write version if not done yet
  328.                     // also write DN of entry
  329.                     if ($is_changed{
  330.                         if (!$this->_version_written{
  331.                             $this->write_version();
  332.                         }
  333.                         $this->_writeDN($entry->dn());
  334.                     }
  335.  
  336.                     // process changes
  337.                     // TODO: consider DN move change!
  338.                     if ($entry->willBeDeleted()) {
  339.                         $this->_writeLine("changetype: delete".PHP_EOL);
  340.                     elseif ($num_of_changes > 0{
  341.                         // write attribute change data
  342.                         $this->_writeLine("changetype: modify".PHP_EOL);
  343.                         foreach ($entry_attrs_changes as $changetype => $entry_attrs{
  344.                             foreach ($entry_attrs as $attr_name => $attr_values{
  345.                                 $this->_writeLine("$changetype$attr_name".PHP_EOL);
  346.                                 if ($attr_values !== null$this->_writeAttribute($attr_name$attr_values$changetype);
  347.                                 $this->_writeLine("-".PHP_EOL);
  348.                             }
  349.                         }
  350.                     }
  351.  
  352.                     // finish this entrys data if we had changes
  353.                     if ($is_changed{
  354.                         $this->_finishEntry();
  355.                     }
  356.                 else {
  357.                     // LDIF-content mode
  358.                     // fetch attributes for further processing
  359.                     $entry_attrs $entry->getValues();
  360.  
  361.                     // sort and put objectclass-attrs to first position
  362.                     if ($this->_options['sort']{
  363.                         ksort($entry_attrs);
  364.                         if (array_key_exists('objectclass'$entry_attrs)) {
  365.                             $oc $entry_attrs['objectclass'];
  366.                             unset($entry_attrs['objectclass']);
  367.                             $entry_attrs array_merge(array('objectclass' => $oc)$entry_attrs);
  368.                         }
  369.                     }
  370.  
  371.                     // write data
  372.                     if (!$this->_version_written{
  373.                         $this->write_version();
  374.                     }
  375.                     $this->_writeDN($entry->dn());
  376.                     foreach ($entry_attrs as $attr_name => $attr_values{
  377.                         $this->_writeAttribute($attr_name$attr_values);
  378.                     }
  379.                     $this->_finishEntry();
  380.                 }
  381.             }
  382.         }
  383.     }
  384.  
  385.     /**
  386.     * Write version to LDIF
  387.     *
  388.     * If the object's version is defined, this method allows to explicitely write the version before an entry is written.
  389.     * If not called explicitely, it gets called automatically when writing the first entry.
  390.     */
  391.     function write_version({
  392.         $this->_version_written = true;
  393.         return $this->_writeLine('version: '.$this->version().PHP_EOL'Net_LDAP_LDIF error: unable to write version');
  394.     }
  395.  
  396.     /**
  397.     * Get or set LDIF version
  398.     *
  399.     * If called without arguments it returns the version of the LDIF file or undef if no version has been set.
  400.     * If called with an argument it sets the LDIF version to VERSION.
  401.     * According to RFC 2849 currently the only legal value for VERSION is 1.
  402.     *
  403.     * @param int $version 
  404.     * @return int 
  405.     */
  406.     function version($version = null{
  407.         if ($version !== null{
  408.             if ($version != 1{
  409.                 $this->_dropError('Net_LDAP_LDIF error: illegal LDIF version set');
  410.             else {
  411.                 $this->_version $version;
  412.             }
  413.         }
  414.         return $this->_version;
  415.     }
  416.  
  417.     /**
  418.     * Returns the file handle the Net_LDAP_LDIF object reads from or writes to.
  419.     *
  420.     * You can, for example, use this to fetch the content of the LDIF file yourself
  421.     *
  422.     * @return null|resource
  423.     */
  424.     function &handle({
  425.         if (!is_resource($this->_FH)) {
  426.             $this->_dropError('Net_LDAP_LDIF error: invalid file resource');
  427.             $null = null;
  428.             return $null;
  429.         else {
  430.             return $this->_FH;
  431.         }
  432.     }
  433.  
  434.     /**
  435.     * Clean up
  436.     *
  437.     * This method signals that the LDIF object is no longer needed.
  438.     * You can use this to free up some memory and close the file handle.
  439.     * The file handle is only closed, if it was opened from Net_LDAP_LDIF.
  440.     */
  441.     function done({
  442.         // close FH if we opened it
  443.         if ($this->_FH_opened{
  444.             fclose($this->handle());
  445.         }
  446.  
  447.         // free variables
  448.         foreach (get_object_vars($thisas $name => $value{
  449.             unset($this->$name);
  450.         }
  451.     }
  452.  
  453.     /**
  454.     * Returns last error message if error was found.
  455.     *
  456.     * Example:
  457.     * <code>
  458.     *  $ldif->someAction();
  459.     *  if ($ldif->error()) {
  460.     *     echo "Error: ".$ldif->error()." at input line: ".$ldif->error_lines();
  461.     *  }
  462.     * </code>
  463.     *
  464.     * @param boolean $as_string If set to true, only the message is returned
  465.     * @return false|Net_LDAP_Error
  466.     */
  467.     function error($as_string = false{
  468.         if (Net_LDAP::isError($this->_error['error'])) {
  469.             return ($as_string)$this->_error['error']->getMessage($this->_error['error'];
  470.         else {
  471.             return false;
  472.         }
  473.     }
  474.  
  475.     /**
  476.     * Returns lines that resulted in error.
  477.     *
  478.     * Perl returns an array of faulty lines in list context,
  479.     * but we always just return an int because of PHPs language.
  480.     *
  481.     * @return int 
  482.     */
  483.     function error_lines({
  484.         return $this->_error['line'];
  485.     }
  486.  
  487.     /**
  488.     * Returns the current Net::LDAP::Entry object.
  489.     *
  490.     * @return Net_LDAP_Entry|false
  491.     * @todo what about file inclusions and urls? "jpegphoto:< file:///usr/local/directory/photos/fiona.jpg"
  492.     */
  493.     function current_entry({
  494.         // parse current lines into an array of attributes and build the entry
  495.         $attributes = array();
  496.         $dn = false;
  497.         foreach ($this->current_lines(as $line{
  498.             preg_match('/^(\w+)(:|::|:<)\s(.+)$/'$line$matches);
  499.             $attr  =$matches[1];
  500.             $delim =$matches[2];
  501.             $data  =$matches[3];
  502.  
  503.             if ($delim == ':'{
  504.                 // normal data
  505.                 $attributes[$attr][$data;
  506.             elseif($delim == '::'{
  507.                 // base64 data
  508.                 $attributes[$attr][base64_decode($data);
  509.             elseif($delim == ':<'{
  510.                 // file inclusion
  511.                 // TODO: Is this the job of the LDAP-client or the server?
  512.                 $this->_dropError('File inclusions are currently not supported');
  513.                 //$attributes[$attr][] = ...;
  514.             else {
  515.                 $this->_dropError('Net_LDAP_LDIF parsing error: invalid syntax at parsing entry line: '.$line);
  516.                 continue;
  517.             }
  518.  
  519.             if (strtolower($attr== 'dn'{
  520.                 // DN line detected
  521.                 $dn $attributes[$attr][0];  // save possibly decoded DN
  522.                 unset($attributes[$attr])// remove wrongly added "dn: " attribute
  523.             }
  524.         }
  525.  
  526.         if (false === $dn{
  527.             $this->_dropError('Net_LDAP_LDIF parsing error: unable to detect DN for entry');
  528.             return false;
  529.         else {
  530.             $newentry Net_LDAP_Entry::createFresh($dn$attributes);
  531.             return $newentry;
  532.         }
  533.     }
  534.  
  535.     /**
  536.     * Returns the lines that generated the current Net::LDAP::Entry object.
  537.     *
  538.     * Note that this returns an empty array if no lines have been read so far.
  539.     *
  540.     * @return array Array of lines
  541.     */
  542.     function current_lines({
  543.         return $this->_lines_cur;
  544.     }
  545.  
  546.     /**
  547.     * Returns the lines that will generate the next Net::LDAP::Entry object.
  548.     *
  549.     * If you set $force to TRUE then you can iterate over the lines that build
  550.     * up entries manually. Otherwise, iterating is done using {@link read_entry()}.
  551.     * Force will move the file pointer forward, thus returning the next entries lines.
  552.     *
  553.     * Wrapped lines will be unwrapped. Comments are stripped.
  554.     *
  555.     * @param boolean $force Set this to true if you want to iterate over the lines manually
  556.     * @return array 
  557.     * @bug Problem with DOS line endings. If the file is a unix one, comment mode works correctly
  558.     */
  559.     function next_lines($force = false{
  560.         // if we already have those lines, just return them, otherwise read
  561.         if (count($this->_lines_next== 0 || $force{
  562.             $this->_lines_next = array()// empty in case something was left (if used $force)
  563.             $entry_done = false;
  564.             $fh =$this->handle();
  565.             $commentmode = false; // if we are in an comment, for wrapping purposes
  566.             $datalines_read = 0;  // how many lines with data we have read
  567.  
  568.             while (!$entry_done && !$this->eof()) {
  569.                 $this->_input_line++;
  570.                 $data fgets($fh);
  571.                 if ($data === false{
  572.                     // error only, if EOF not reached after fgets() call
  573.                     if  (!$this->eof()) {
  574.                         $this->_dropError('Net_LDAP_LDIF error: error reading from file at input line '.$this->_input_line$this->_input_line);
  575.                     }
  576.                     break;
  577.                 else {
  578.                     if (count($this->_lines_next> 0 && preg_match('/^$/'$data)) {
  579.                         // Entry is finished if we have an empty line after we had data
  580.                         $entry_done = true;
  581.  
  582.                         // Look ahead if the next EOF is nearby. Comments and empty
  583.                         // lines at the file end may cause problems otherwise
  584.                         $current_pos ftell($fh);
  585.                         $data        fgets($fh);
  586.                         while (!feof($fh)) {
  587.                             if (preg_match('/^\s*$/'$data|| preg_match('/^#/'$data)) {
  588.                                 // only empty lines or comments, continue to seek
  589.                                 // TODO: Known bug: Wrappings for comments are okay but are treaten as
  590.                                 //       error, since we do not honor comment mode here.
  591.                                 //       This should be a very theoretically case, however
  592.                                 //       i am willing to fix this if really necessary.
  593.                                 $this->_input_line++;
  594.                                 $current_pos ftell($fh);
  595.                                 $data        fgets($fh);
  596.                             else {
  597.                                 // Data found if non emtpy line and not a comment!!
  598.                                 // Rewind to position prior last read and stop lookahead
  599.                                 fseek($fh$current_pos);
  600.                                 break;
  601.                             }
  602.                         }
  603.                         // now we have either the file pointer at the beginning of
  604.                         // a new data position or at the end of file causing feof() to return true
  605.  
  606.                     else {
  607.                         // build lines
  608.                         if (preg_match('/^version:\s(.+)$/'$data$match)) {
  609.                             // version statement, set version
  610.                             $this->version($match[1]);
  611.                         elseif (preg_match('/^\w+::?\s.+$/'$data)) {
  612.                             // normal attribute: add line
  613.                             $commentmode         = false;
  614.                             $this->_lines_next[trim($data);
  615.                             $datalines_read++;
  616.                         elseif (preg_match('/^\s(.+)$/'$data$matches)) {
  617.                             // wrapped data: unwrap if not in comment mode
  618.                             if (!$commentmode{
  619.                                 if ($datalines_read == 0{
  620.                                     // first line of entry: wrapped data is illegal
  621.                                     $this->_dropError('Net_LDAP_LDIF error: illegal wrapping at input line '.$this->_input_line$this->_input_line);
  622.                                 else {
  623.                                     $last array_pop($this->_lines_next);
  624.                                     $last $last.trim($matches[1]);
  625.                                     $this->_lines_next[$last;
  626.                                     $datalines_read++;
  627.                                 }
  628.                             }
  629.                         elseif (preg_match('/^#/'$data)) {
  630.                             // LDIF comments
  631.                             $commentmode = true;
  632.                         elseif (preg_match('/^\s*$/'$data)) {
  633.                             // empty line but we had no data for this
  634.                             // entry, so just ignore this line
  635.                             $commentmode = false;
  636.                         else {
  637.                             $this->_dropError('Net_LDAP_LDIF error: invalid syntax at input line '.$this->_input_line$this->_input_line);
  638.                             continue;
  639.                         }
  640.  
  641.                     }
  642.                 }
  643.             }
  644.         }
  645.         return $this->_lines_next;
  646.     }
  647.  
  648.     /**
  649.     * Convert an attribute and value to LDIF string representation
  650.     *
  651.     * It honors correct encoding of values according to RFC 2849.
  652.     * Line wrapping will occur at the configured maximum but only if
  653.     * the value is greater than 40 chars.
  654.     *
  655.     * @access private
  656.     * @param string $attr_name  Name of the attribute
  657.     * @param string $attr_value Value of the attribute
  658.     * @return string LDIF string for that attribute and value
  659.     */
  660.     function _convertAttribute($attr_name$attr_value{
  661.         // Handle empty attribute or process
  662.         if (strlen($attr_value== 0{
  663.             $attr_value " ";
  664.         else {
  665.             $base64 = false;
  666.             // ASCII-chars that are NOT safe for the
  667.             // start and for being inside the value.
  668.             // These are the int values of those chars.
  669.             $unsafe_init = array(01013325860);
  670.             $unsafe      = array(01013);
  671.  
  672.             // Test for illegal init char
  673.             $init_ord ord(substr($attr_value01));
  674.             if ($init_ord >= 127 || in_array($init_ord$unsafe_init)) {
  675.                 $base64 = true;
  676.             }
  677.  
  678.             // Test for illegal content char
  679.             for ($i = 0; $i strlen($attr_value)$i++{
  680.                 $char substr($attr_value$i1);
  681.                 if (ord($char>= 127 || in_array($init_ord$unsafe)) {
  682.                     $base64 = true;
  683.                 }
  684.             }
  685.  
  686.             // Test for ending space
  687.             if (substr($attr_value-1== ' '{
  688.                 $base64 = true;
  689.             }
  690.  
  691.             // if converting is needed, do it
  692.             if ($base64{
  693.                 $attr_name .= ':';
  694.                 $attr_value base64_encode($attr_value);
  695.             }
  696.  
  697.             // lowercase attr names if requested
  698.             if ($this->_options['lowercase']$attr_name strtolower($attr_name);
  699.  
  700.             // handle line wrapping
  701.             if ($this->_options['wrap'> 40 && strlen($attr_value$this->_options['wrap']{
  702.                 $attr_value wordwrap($attr_value$this->_options['wrap']PHP_EOL." "true);
  703.             }
  704.         }
  705.  
  706.         return $attr_name.': '.$attr_value;
  707.     }
  708.  
  709.     /**
  710.     * Convert an entries DN to LDIF string representation
  711.     *
  712.     * It honors correct encoding of values according to RFC 2849.
  713.     *
  714.     * @access private
  715.     * @param string $dn  UTF8-Encoded DN
  716.     * @return string LDIF string for that DN
  717.     * @todo I am not sure, if the UTF8 stuff is correctly handled right now
  718.     */
  719.     function _convertDN($dn{
  720.         $base64 = false;
  721.         // ASCII-chars that are NOT safe for the
  722.         // start and for being inside the dn.
  723.         // These are the int values of those chars.
  724.         $unsafe_init = array(01013325860);
  725.         $unsafe      = array(01013);
  726.  
  727.         // Test for illegal init char
  728.         $init_ord ord(substr($dn01));
  729.         if ($init_ord >= 127 || in_array($init_ord$unsafe_init)) {
  730.             $base64 = true;
  731.         }
  732.  
  733.         // Test for illegal content char
  734.         for ($i = 0; $i strlen($dn)$i++{
  735.             $char substr($dn$i1);
  736.             if (ord($char>= 127 || in_array($init_ord$unsafe)) {
  737.                 $base64 = true;
  738.             }
  739.         }
  740.  
  741.         // Test for ending space
  742.         if (substr($dn-1== ' '{
  743.             $base64 = true;
  744.         }
  745.  
  746.         // if converting is needed, do it
  747.         return ($base64)'dn:: '.base64_encode($dn'dn: '.$dn;
  748.     }
  749.  
  750.     /**
  751.     * Writes an attribute to the filehandle
  752.     *
  753.     * @access private
  754.     * @param string $attr_name 
  755.     * @param string|array$attr_values Single attribute value or array with attribute values
  756.     */
  757.     function _writeAttribute($attr_name$attr_values{
  758.         // write out attribute content
  759.         if (!is_array($attr_values)) {
  760.             $attr_values = array($attr_values);
  761.         }
  762.         foreach ($attr_values as $attr_val{
  763.             $line $this->_convertAttribute($attr_name$attr_val).PHP_EOL;
  764.             $this->_writeLine($line'Net_LDAP_LDIF error: unable to write attribute '.$attr_name.' of entry '.$this->_entrynum);
  765.         }
  766.     }
  767.  
  768.     /**
  769.     * Writes a DN to the filehandle
  770.     *
  771.     * Depending on
  772.     *
  773.     * @access private
  774.     * @param string $dn 
  775.     */
  776.     function _writeDN($dn{
  777.         // prepare DN
  778.         if ($this->_options['encode'== 'base64'{
  779.             $dn $this->_convertDN($dn).PHP_EOL;
  780.         elseif ($this->_options['encode'== 'canonical'{
  781.             $dn Net_LDAP_Util::canonical_dn($dnarray('casefold' => 'none') ).PHP_EOL;
  782.         else {
  783.             $dn $dn.PHP_EOL;
  784.         }
  785.         $this->_writeLine($dn'Net_LDAP_LDIF error: unable to write DN of entry '.$this->_entrynum);
  786.     }
  787.  
  788.     /**
  789.     * Finishes an LDIF entry
  790.     *
  791.     * @access private
  792.     */
  793.     function _finishEntry({
  794.         $this->_writeLine(PHP_EOL'Net_LDAP_LDIF error: unable to close entry '.$this->_entrynum);
  795.     }
  796.  
  797.     /**
  798.     * Just write an arbitary line to the filehandle
  799.     *
  800.     * @access private
  801.     * @param string $line  Content to write
  802.     * @param string $error If error occurs, drop this message
  803.     * @return true|false
  804.     */
  805.     function _writeLine($line$error 'Net_LDAP_LDIF error: unable to write to filehandle'{
  806.         if (is_resource($this->handle()) && fwrite($this->handle()$linestrlen($line)) === false{
  807.             $this->_dropError($error);
  808.             return false;
  809.         else {
  810.             return true;
  811.         }
  812.     }
  813.  
  814.     /**
  815.     * Optionally raises an error and pushes the error on the error cache
  816.     *
  817.     * @access private
  818.     * @param string $msg  Errortext
  819.     * @param int    $line Line in the LDIF that caused the error
  820.     */
  821.     function _dropError($msg$line = null{
  822.         $this->_error['error'= new Net_LDAP_Error($msg);
  823.         if ($line !== null$this->_error['line'$line;
  824.  
  825.         if ($this->_options['onerror'== 'die'{
  826.             die($msg.PHP_EOL);
  827.         elseif ($this->_options['onerror'== 'warn'{
  828.             echo $msg.PHP_EOL;
  829.         }
  830.     }
  831. }
  832. ?>

Documentation generated on Mon, 11 Mar 2019 15:14:44 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.