<?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
 */
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 LdifEntry($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;
	}
}
?>