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

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