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

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