<?php
// $Header: /cvsroot/phpldapadmin/phpldapadmin/lib/ldif_functions.php,v 1.28 2005/12/10 10:34:55 wurley Exp $
 
/**
 * @todo put the display_parse_error method in ldif_import here
 * @todo do not allow entry whose number of unfolded lines is
 *        over 200 for example
 * @todo later: return the entry as an array before returning the entry object
 *        (just make a wrapper method)
 * @todo make a class CSVReader wich extends FileReader
 *
 * @package phpLDAPadmin
 *
 * @author The phpLDAPadmin development team
 */
 
/**
 * This class represente an entry in the ldif file
 * @package phpLDAPadmin
 */
  // Check to ensure this file is included in osConcert!
defined('_FEXEC') or die(); 
 
class LdifEntry {
    var $dn;
    var $changeType;
    var $attributes = array();
 
    /**
     * Creates a new LdifEntry, with optionally specified DN, changeType, and attributes.
     *
     * @param String $dn   the distinguished name of the entry. Default is set to an empty string
     * @param String $changeType the change type associated with the entry. Default is set to add.
     * @param String[]  $atts the attributes of the entry
     */
    function __construct($dn='',$changeType='add',$atts = array()) {
        $this->dn = $dn;
        $this->changeType = $changeType;
        $this->attributes = $atts;
    }
 
    /**
     * Return the dn of the entry
     *
     * @return String the dn of the entry
     */
    function getDn() {
        return $this->dn;
    }
 
    /**
     * Setter method for the distinguished name
     *
     * @param String $dn the distinguished name of the entry
     */
    function setDn($dn) {
        $this->dn = $dn;
    }
 
    /**
     * Getter method for the change type
     *
     * @return String the change type of the entry
     */
    function getChangeType() {
        return $this->changeType;
    }
 
    /**
     * Setter method for the change type of the entry
     *
     * @param String $changeType the change type of the entry
     */
    function setChangeType($changeType) {
        $this->changeType = $changeType;
    }
 
    /**
     * Add the attributes to the entry
     *
     * @param String[][] $atts the attributes of the entry
     */
    function setAttributes($atts) {
        $this->attributes = $atts;
    }
 
    /**
     * Get the attributes of the entry
     *
     * @return String[][] the attributes of the entry
     */
    function getAttributes() {
        return $this->attributes;
 
    }
}
 
/**
 * This exception is similar to the one in LdifReader
 * Should be remove latter
 * see comment for the class Ldif_LdapEntryReader
 * @package phpLDAPadmin
 */
class LdifEntryReaderException {
    var $lineNumber;
    var $currentLine;
    var $message;
 
    /**
     * Constructor of the exception
     *
     * @param int $lineNumber the number of the line where
     *         the error occured
     * @param String currentLine the line wich raised an exception
     * @param String the message associated the exception
     */
 
    function LdifEntryReaderException($lineNumber,$currentLine,$message) {
        $this->lineNumber = $lineNumber;
        $this->currentLine =$currentLine;
        $this->message = $message;
    }
}
 
/**
 * Class in charge of reading a paricular entry
 * @package phpLDAPadmin
 */
class LdifEntryReader {
    # The entry
        var $entry;
 
    # The lines of the entry fetch from the file
        var $lines;
 
    # The dn of the entry
        var $dn='';
 
    # Error flag
        var $_error;
 
    # The current line number of the entry;
        var $_currentLineNumber;
 
    /**
     * Constructor of the LdifEntryReader
     *
     * @param String[] $lines the line of the entry
     */
    function LdifEntryReader() {}
 
    function readLines(&$lines) {
        $this->lines = &$lines;
        $this->_currentLineNumber = 1;
        $this->_error = 0;
    }
 
    /**
     * Read the change type action associated with the entry
     *
     * @return String the change type action of the entry
     */
    function _readChangeType() {
        $changeType = 'add';
        $arr = array();
 
        # No lines after the dn one
        if (count($this->lines) == 0) {
            $this->lines[0] = '';
            $this->setLdifEntryReaderException(
                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                    _('Missing attibutes or changetype attribute for entry'),new LdifEntry($this->dn)));
 
        # Get the change type of the entry
        } elseif (ereg('changetype:[ ]*(delete|add|modrdn|moddn|modify)',$this->lines[0],$arr)) {
            $changeType = $arr[1];
            array_shift($this->lines);
            $this->_currentLineNumber++;
        }
 
        return $changeType;
    }
 
    /**
     * Check if the distinguished name is base 64 encoded
     *
     * @return boolean true if the dn is base 64 encoded, false otherwise
     */
    function _isDnBase64Encoded($dn) {
        return ereg('dn::',$dn) ? 1 : 0;
    }
 
    /**
     * Return the base64 decoded value of an attribute
     *
     * @param $attr the attribute to be decoded
     * @return String base64 decoded value of an attribute
     */
    function _getBase64DecodedValue($attr) {
        return base64_decode(trim($attr));
    }
 
    /**
     * Fetch the dn value from a line of the ldif file
     *
     * @param String $currentDnLine line with a distinguished name
     * @return the value of the distinguished name
     */
    function _getDnValue() {
        $currentDnLine = $this->lines[0];
        if ($this->_isDNBase64Encoded($currentDnLine))
            $currentDnValue = $this->_getBase64DecodedValue(substr($currentDnLine,4,strlen($currentDnLine)-1));
        else
            $currentDnValue = substr($currentDnLine,3,strlen($currentDnLine)-1);
 
        # switch to the next line
        array_shift($this->lines);
        $this->_currentLineNumber++;
 
        return trim($currentDnValue);
    }
 
    /**
     * Check if the dn line is valid
     *
     * @return boolean true if the dn is valid, false otherwise.
     */
    function isValidDn() {
        return ereg('^dn:',$this->lines[0]) ? 1 : 0;
    }
 
    /**
     * Return the entry read from the ldif lines
     *
     * @return LdifEntry the entry
     */
    function getEntry() {
        # the dn is not valid, throw the exception and return the entry with the non valid dn
        if (! $this->isValidDn()) {
            $dn = $this->lines[0];
            $changeType = $this->_readChangeType();
 
            # For the moment, overwrite the exception
            $this->setLdifEntryReaderException(
                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                    _('A valid dn line is required.'),new LdifEntry($this->dn)));
 
            return new LdifEntry($dn,$changeType);
        }
 
        $dn = $this->_getDnValue();
        $changeType = $this->_readChangeType();
        $this->entry = new LdifEntry($dn,$changeType);
 
        if ($changeType == 'add') {
            $this->_getAddAttributes();
 
        } elseif ($changeType == 'delete') {
            # Do nothing
 
        } elseif ($changeType == 'modrdn' || $changeType == 'moddn') {
            $this->_getModrdnAttributes();
 
        } elseif ($changeType == 'modify') {
            $this->_getModifyAttributes();
        }
 
        return $this->entry;
    }
 
    /**
     * Checked if the parsing of the entry has raised some exception
     *
     * @return bool true if the reading of the entry raised some exceptions, else otherwise.
     */
    function hasRaisedException() {
        return $this->_error;
    }
 
    /**
     * Set the exception handler for the entry reader
     *
     * @param Ldap_ldifEntryReaderException the exception handler associate
     *  with the parsing error.
     */
    function setLdifEntryReaderException($ldifEntryReaderException) {
        $this->_error=1;
        $this->_ldifEntryReaderException= $ldifEntryReaderException;
    }
 
    /**
     * Return the exception handler of the entry Reader
     *
     * @return Ldap_LdifEntryReaderException the exception associate with
     *  the exception wich occur during parsing the file.
     */
    function getLdifEntryReaderException() {
        return $this->_ldifEntryReaderException;
    }
 
    /**
     * Method to retrieve the attribute value of a ldif line,
     * and get the base 64 decoded value if it is encoded
     */
    function _getAttributeValue($attributeValuePart) {
        $attribute_value = '';
 
        if (substr($attributeValuePart,0,1) == ':') {
            $attribute_value = $this->_getBase64DecodedValue(trim(substr($attributeValuePart,1)));
 
        } elseif (substr($attributeValuePart,0,1) == '<') {
            /* we need to handle the case for the scheme "file://" as it
               doesn't seem to be supported by fopen */
 
            $file_path_with_scheme = trim(substr($attributeValuePart,1));
            if (ereg('^file://',$file_path_with_scheme)) {
 
                $file_path = substr(trim($file_path_with_scheme),7);
                if ($handle = @fopen($file_path,'rb')) {
                    if (! $attribute_value = @fread($handle,filesize($file_path))) {
                        $this->setLdifEntryReaderException(
                            new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                                _('Unable to read file'),$this->entry));
                    }
                    @fclose($handle);
 
                } else {
                    $this->setLdifEntryReaderException(
                        new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                            _('Unable to open file'),$this->entry));
                }
 
            } else {
                $this->setLdifEntryReaderException(
                    new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                        _('The url attribute value should begin with file:///'),$this->entry));
            }
 
        # It's a string
        } else {
            $attribute_value = $attributeValuePart;
        }
 
        return trim($attribute_value);
    }
 
    /**
     * Build the attributes array when the change type is add.
     */
    function _getAddAttributes() {
        if (count($this->lines) == 0) {
            $this->lines[0] = '';
            $this->setLdifEntryReaderException(
                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                    _('Missing attributes for the entry'),$this->entry));
        }
 
        while (count($this->lines)!=0 &&$this->_error!=1) {
            $currentLine = &$this->lines[0];
 
            if (ereg(':',$currentLine)) {
                # get the position of the  character  ":"
                $pos = strpos($currentLine,':');
 
                # get the description of the attribute
                $attributeDescription =  substr($currentLine,0, $pos);
 
                # get the value part of the attribute
                $attribute_value_part = trim(substr($currentLine,$pos+1,strlen($currentLine)));
                $attribute_value = $this->_getAttributeValue($attribute_value_part);
                $this->entry->attributes[$attributeDescription][] = trim($attribute_value);
                array_shift($this->lines);
                $this->_currentLineNumber++;
 
            } else {
                $this->setLdifEntryReaderException(
                    new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                        _('Attribute not well formed'),$this->entry));
 
                //jetter l'exception
            }
        }
    }
 
    /**
     * Build the attributes array for the entry when the change type is modify
     */
    function _getModifyAttributes() {
        if (count($this->lines)==0) {
            $this->lines[0] = '';
            $this->setLdifEntryReaderException(
                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                    _('Missing attributes for the entry'),$this->entry));
        }
 
        $numberModification = 0;
 
        # while the array is not empty
        while (count($this->lines) != 0 && $this->_error != 1) {
            $new_entry_mod = 0;
 
            # get the current line with the action
            $currentLine = &$this->lines[0];
            $attribute= explode(':',$currentLine);
 
            if (count($attribute) == 2) {
                $action_attribute = trim($attribute[0]);
                $action_attribute_value = trim($attribute[1]);
 
                if ($action_attribute != 'add' && $action_attribute != 'delete' && $action_attribute != 'replace') {
                    $this->setLdifEntryReaderException(
                        new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                            _('The attribute name should be add, delete or replace'),$this->entry));
                }
 
                # put the action attribute in the array
                $this->entry->attributes[$numberModification] = array();
                $this->entry->attributes[$numberModification][$action_attribute] =
                     $this->_getAttributeValue($action_attribute_value);
                $this->entry->attributes[$numberModification][$action_attribute_value] = array();
 
                # fetching the attribute for the following line
                array_shift($this->lines);
                $currentLine = &$this->lines[0];
                $this->_currentLineNumber++;
 
                while (trim($currentLine)!= '-' && $this->_error != 1 && count($this->lines)!=0) {
                    # if there is a valid line
                    if (ereg(':',$currentLine)) {
                        # get the position of the character ":"
                        $pos = strpos($currentLine,':');
 
                        # get the name of the attribute to modify
                        $attribute_name = substr($currentLine,0,$pos);
 
                        # check that it correspond to the one specified before
                        if ($attribute_name == $action_attribute_value) {
                            # get the value part of the attribute
                            $attribute_value_part = trim(substr($currentLine,$pos+1,strlen($currentLine)));
                            $attribute_value = $this->_getAttributeValue($attribute_value_part);
                            $this->entry->attributes[$numberModification][$attribute_name][] = $attribute_value;
                            $this->_currentLineNumber++;
                            array_shift($this->lines);
 
                            if (count($this->lines) != 0)
                                $currentLine = &$this->lines[0];
 
                        } else {
                            $this->setLdifEntryReaderException(
                                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                                    sprintf(_('The attribute to modify doesnt match the one specified by the %s attribute.'),$action_attribute),
                                    $this->entry));
                        }
 
                    } else {
                        $this->setLdifEntryReaderException(
                            new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                                _('Attribute is not valid'),$this->entry));
                    }
                } # end inner while
 
                # we get a "-" charachter, we remove it from the array
                if ($currentLine == '-') {
                    array_shift($this->lines);
                    $this->_currentLineNumber++;
                }
 
                $numberModification++;
            }
        }
    }
 
    /**
     * Build the attributes for the entry when the change type is modrdn
     */
    function _getModrdnAttributes() {
        $attrs = array();
        $numLines = count($this->lines);
 
        if ($numLines != 2 && $numLines !=3) {
            if ($numLines==0)
                $this->lines[0] = '';
 
            $this->setLdifEntryReaderException(
                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                _('The entry is not valid'),$this->entry));
 
        } else {
            $currentLine = $this->lines[0];
 
            # first we need to check if there is an new rdn specified
            if (ereg('^newrdn:(:?)',$currentLine)) {
                $attributeValue = $this->_getAttributeValue(trim(substr($currentLine,7)));
                $attrs['newrdn']=$attributeValue;
 
                # /switch to the deleteoldrdn attribute
                array_shift($this->lines);
                $this->_currentLineNumber++;
                $arr=array();
 
                if(ereg('^deleteoldrdn:[ ]*(0|1)',$this->lines[0],$arr)) {
                    $attrs['deleteoldrdn'] = $arr[1];
 
                    # switch to the possible new superior attribute
                    if ($numLines>2) {
                        array_shift($this->lines);
                        $this->_currentLineNumber++;
                        $currentLine = $this->lines[0];
 
                        # then the possible new superior attribute
                        if(ereg('^newsuperior:',$currentLine)) {
                            $attrs['newsuperior'] = $this->_getAttributeValue(trim(substr($currentLine,12)));
 
                        } else {
                            $this->setLdifEntryReaderException(
                                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                                _('The attribute name should be newsuperior'),$this->entry));
                        }
 
                    } else {
                        # As the first character is not ,,we "can write it this way for teh moment"
                        if ($pos = strpos($this->entry->dn,',')) {
                            $attrs['newsuperior'] = substr($this->entry->dn,$pos+1);
 
                        } else {
                            $this->setLdifEntryReaderException(
                                new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                                    _('Container is null'),$this->entry));
                        }
                    }
 
                } else {
                    $this->setLdifEntryReaderException(
                        new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                            _('A valid deleteoldrdn attribute should be specified'),$this->entry));
                }
 
            } else {
                $this->setLdifEntryReaderException(
                    new LdifEntryReaderException($this->_currentLineNumber,$this->lines[0],
                        _('A valid newrdn attribute  should be specified'),$this->entry));
            }
 
            $this->entry->attributes = $attrs;
        }
    }
}
 
/**
 * Exception which can be raised during processing the ldif file
 * @package phpLDAPadmin
 */
class LdifReaderException {
    var $lineNumber;
    var $message;
    var $currentLine;
 
    /**
     * Constructor of the exception
     *
     * @param int $lineNumber the number of the line where the error occured
     * @param String currentLine the line wich raised an exception
     * @param String the message associated the exception
     */
    function LdifReaderException($lineNumber,$currentLine,$message) {
        $this->lineNumber = $lineNumber;
        $this->currentLine =$currentLine;
        $this->message = $message;
    }
}
 
/**
 * Helper base class to read file.
 * @package phpLDAPadmin
 */
class Reader {
    # The current line number
        var $_currentLineNumber;
 
    # The current line
        var $_currentLine;
 
    # The current entry
        var $_currentEntry;
 
    # Array containing the lines of the current entry
        var $_currentLines;
 
    # Warning message. Only use for the verion number
        var $_warningMessage;
 
    # Warning version number.
        var $_warningVersion;
 
    # Boolean flag for error
        var $_error;
 
    # Continuous mode operation flag
        var $continuous_mode;
 
    /**
     * Check if the ldif version is present in the ldif
     *
     * @return true if a version line was found,false otherwise
     */
    function hasVersionNumber() {
        $ldifLineFound = 0;
 
        while (! $this->eof() && ! $ldifLineFound) {
 
            # fetch the first line
            $this->_nextLine();
 
            # if it's a ldif comment line or a blank line,leave it and continue
            if ($this->_isCommentLine() || $this->_isBlankLine()) {
                # Do nothing
 
            } elseif (ereg('^version',trim($this->_currentLine))) {
                $ldifLineFound = 1;
                $this->_nextLine();
 
            } else {
                $this->_warningVersion = 1;
                $this->_warningMessage = _('No version found. Assuming 1.');
                $ldifLineFound = 1;
            }
        } # end while loop
 
        return $this->_warningVersion;
    }
 
    function getWarningMessage() {
        return $this->_warningMessage;
    }
 
    /**
     * Check if is the current line is a blank line.
     *
     * @return bool if it is a blank line,false otherwise.
     */
    function _isBlankLine() {
        return(trim($this->_currentLine) == '') ? 1 : 0;
    }
 
    /**
     * Private constructor of the LDIFReader class.
     * Marked as private as we need to instantiate the class
     * by using other constructor with parameters
     * Use to initialize instance members.
     */
    function _LdifReader() {
        $this->_error = 0;
        $this->_warning = 0;
        $this->_currentLineNumber = 0;
        $this->_currentLines = array();
        $this->_warningMessage = '';
        $this->_warningVersion = '';
        $this->ldifEntryReader = new LdifEntryReader();
        $this->_currentEntry = new LdifEntry();
    }
 
    /**
     * Returns the lines that generated the Ldif Entry.
     *
     * @return String[] The lines from the entry.
     */
    function getCurrentLines() {
        return $this->_currentLines;
    }
 
    /**
     * Check if it's a ldif comment line.
     *
     * @return bool true if it's a comment line,false otherwise
     */
    function _isCommentLine() {
        return substr(trim($this->_currentLine),0,1) == '#' ? 1 : 0;
    }
 
    /**
     * Check if the current line is a line containing the distinguished
     * name of an entry.
     *
     * @return bool true if the line contains a dn line, false otherwise.
     */
    function _isDnLine() {
        return ereg('^dn:',$this->_currentLine) ? 1 : 0;
    }
 
    /**
     * Return the current entry as an object
     *
     * @return Ldap_Ldif_entry the current ldif entry
     */
    function fetchEntryObject() {
        return $this->_currentEntry;
    }
 
    /**
     * Return the current entry as an array
     *
     */
    function fetchEntryArray() {}
 
    /**
     * Return the entry as it is.
     *
     * @return String[] The lines from the entry.
     */
    function fetchEntryRaw() {
        return getCurrentLines();
    }
 
    /**
     * Get the lines of the next entry
     *
     * @return String[] the lines (unfolded) of the next entry
     */
    function nextLines() {
        $endEntryFound = 0;
        # Free the array (instance member)
        unset($this->_currentLines);
 
        # Reset the error state
        $this->_error = 0;
 
        if ($this->_hasMoreEntries() && !$this->eof()) {
            # the first line is the dn one
            $this->_currentLines[0]= trim($this->_currentLine);
 
            $count=0;
 
            # while we end on a blank line, fetch the attribute lines
            while (!$this->eof() && !$endEntryFound) {
                # fetch the next line
                $this->_nextLine();
 
                /* If the next line begin with a space, we append it to the current row
                   else we push it into the array (unwrap)*/
 
                if (substr($this->_currentLine,0,1) == ' ') {
                    $this->_currentLines[$count] .= trim($this->_currentLine);
 
                } elseif (substr($this->_currentLine,0,1) == '#') {
                    # Do nothing
 
                } elseif (trim($this->_currentLine) != '') {
                    $this->_currentLines[++$count] = trim($this->_currentLine);
 
                } else {
                    $endEntryFound=1;
                }
            } # end while
 
            # return the ldif entry array
             return $this->_currentLines;
 
        } else {
            return false;
        }
    }
 
    /**
     * Private method to check if there is more entries in the file
     *
     * @return boolean true if an entry was found, false otherwise.
     */
    function _hasMoreEntries() {
        $entry_found = 0;
 
        while (!$this->eof() && !$entry_found) {
            # if it's a comment or blank line,switch to the next line
            if ($this->_isCommentLine() || $this->_isBlankLine()) {
                # Do nothing
                $this->_nextLine();
 
            } else {
                $this->_currentDnLine = $this->_currentLine;
                $this->dnLineNumber = $this->_currentLineNumber;
                $entry_found = 1;
            }
        }
        return $entry_found;
    }
 
    /**
     * Associate the ldif reader with a exception which occurs during
     * proceesing the file.
     * Easier to do exception if we had to switch to php5
     *
     * @param Ldap_LdifReaderException $ldifReaderException 
     */
    function setLdapLdifReaderException($ldifReaderException) {
        $this->_ldifReaderException = $ldifReaderException;
 
        if (!$this->continuous_mode)
            $this->done();
 
        $this->_error = 1;
    }
 
    /**
     * Return the exception raised during processing the file
     *
     * @return Ldap_ldifReaderException 
     */
    function getLdapLdifReaderException() {
        return $this->_ldifReaderException;
    }
 
    /**
     * Helper method which return the value
     * of the instance member $_error
     *
     * @return true if an error was encountered, false otherwise
     */
    function _ldifHasError() {
        return $this->_error;
    }
 
    function hasRaisedException() {
        return $this->_error;
    }
 
    /**
     * Return a ldif entry object
     *
     * @return LdifEntry the entry object buid from the lines of the ldif file
     */
    function readEntry() {
        if ($lines = $this->nextLines()) {
            $this->ldifEntryReader->readLines($lines);
 
            # fetch entry
            $entry = $this->ldifEntryReader->getEntry();
            $this->_currentEntry = $entry;
 
            # if any exception has raised, catch it and throw it to the main reader
            if ($this->ldifEntryReader->hasRaisedException()) {
                $exception = $this->ldifEntryReader->getLdifEntryReaderException();
                $faultyLineNumber = $this->dnLineNumber + $exception->lineNumber - 1;
                $this->setLdapLdifReaderException(
                    new LdifReaderException($faultyLineNumber,$exception->currentLine,$exception->message));
 
                if (! $this->continuous_mode)
                    return 0;
            }
            return $entry;
 
        } else
            return false;
    }
}
 
/**
 * Helper base class to read file.
 * @package phpLDAPadmin
 */
class FileReader extends Reader {
    # The file pointer
        var $_fp;
 
    /**
     * Constructor of the FileReader class
     * @param $path2File 
     */
    function FileReader($path2File) {
        $this->_fp = fopen($path2File,'r');
    }
 
    /**
     * Returns true if we reached the end of the file.
     *
     * @return bool true if it's the end of file, false otherwise.
     */
    function eof() {
        return @feof($this->_fp);
    }
 
    /**
     * Helper method to switch to the next line
     */
    function _nextLine() {
        $this->_currentLineNumber++;
        $nextLine = fgets($this->_fp,1024);
        $this->_currentLine = $nextLine;
 
        # Need to check if it is a very long line (not folded line for example)
        while (!ereg("\n|\r\n|\r",$nextLine) && !$this->eof()) {
            $nextLine = fgets($this->_fp,1024);
            $this->_currentLine .= trim($nextLine);
        }
    }
 
    /**
     * Close the handler
     */
    function close() {
        return @fclose($this->_fp);
    }
}
 
/**
 * Main parser of the ldif file
 * @package phpLDAPadmin
 */
class LdifReader extends FileReader {
    /**
     * Constructor of the class
     *
     * @param String $path2File path of the ldif file to read
     * @param boolean $continuous_mode 1 if continuous mode operation, 0 otherwise
     */
    function LdifReader($path2File,$continuous_mode=0) {
        parent::FileReader($path2File);
        $this->continuous_mode = $continuous_mode;
        $this->_LdifReader();
    }
 
    /**
     * Close the ldif file
     *
     * @return void 
     */
    function done() {
        if (!$this->_ldifHasError())
            @fclose($this->_fp);
    }
}
 
class LdifReaderStdIn extends Reader {
    /**
     * Constructor of the class
     *
     * @param String $path2File path of the ldif file to read
     * @param boolean $continuous_mode 1 if continuous mode operation, 0 otherwise
     */
    function LdifReaderStdIn($ldif,$continuous_mode=0) {
        $this->_ldif = explode("\n",$ldif);
        $this->continuous_mode = $continuous_mode;
        $this->_LdifReader();
    }
 
    /**
     * Helper method to switch to the next line
     */
    function _nextLine() {
        $this->_currentLineNumber++;
        $nextLine = array_shift($this->_ldif);
        $this->_currentLine = $nextLine;
    }
 
    /**
     * Returns true if we reached the end of the file.
     *
     * @return bool true if it's the end of file, false otherwise.
     */
    function eof() {
        return count($this->_ldif) > 0 ? 0 : 1;
    }
 
    /**
     * Close the ldif
     *
     * @return void 
     */
    function done() {}
}
 
/**
 * Helper class to write entries into the ldap server
 * @package phpLDAPadmin
 */
class LdapWriter {
    var $ldapserver;
    var $_writeError=0;
 
    /**
     * Constructor
     */
    function LdapWriter(&$ldapserver) {
        $this->ldapserver = &$ldapserver;
    }
 
    /**
     * Add a new entry to the ldap server
     *
     * @param LdifEntry $entry the entry to add
     * @return true in case of success, false otherwise
     */
    function ldapAdd($entry) {
        return $this->ldapserver->add($entry->dn,$entry->attributes);
    }
 
    /**
     * Modify an entry
     *
     * @param LdifEntry $entry the entry to add
     * @return true in case of success, false otherwise
     */
    function ldapModify($entry) {
        $changeType = $entry->getChangeType();
 
        switch ($changeType) {
            case 'add':
                $this->_writeError= $this->ldapAdd($entry);
                break;
 
            case 'delete':
                $this->_writeError = $this->ldapserver->delete($entry->dn);
                break;
 
            case 'modrdn':
                $atts=$entry->getAttributes();
                $this->_writeError = $this->ldapserver->rename($entry->dn,$atts['newrdn'],$atts['newsuperior'],$atts['deleteoldrdn']);
                break;
 
            case 'moddn':
                $atts=$entry->getAttributes();
                $this->_writeError = $this->ldapserver->rename($entry->dn,$atts['newrdn'],$atts['newsuperior'],$atts['deleteoldrdn']);
                break;
 
            case 'modify':
                $attributes = $entry->attributes;
                $num_attributes = count($attributes);
 
                for ($i=0;$i<$num_attributes;$i++) {
                    $action = key($attributes[$i]);
                    array_shift($attributes[$i]);
                    switch ($action) {
                        case 'add':
                            $this->_writeError = $this->ldapserver->attrModify($entry->dn,$attributes[$i]);
                            break;
 
                        case 'delete':
                            $this->_writeError = $this->ldapserver->attrDelete($entry->dn,$attributes[$i]);
                            break;
 
                        case 'replace':
                            $this->_writeError = $this->ldapserver->attrReplace($entry->dn,$attributes[$i]);
                            break;
                    }
                }
        }
 
        return $this->_writeError;
    }
}
?>
