+// $OpenLDAP$
/*
- * Copyright 2008, OpenLDAP Foundation, All Rights Reserved.
+ * Copyright 2008-2013 The OpenLDAP Foundation, All Rights Reserved.
* COPYING RESTRICTIONS APPLY, see COPYRIGHT file
*/
#include "debug.h"
#include <string>
+#include <sstream>
+#include <stdexcept>
#include <sasl/saslutil.h> // For base64 routines
-LdifReader::LdifReader( std::istream &input ) : m_ldifstream(input)
+typedef std::pair<std::string, std::string> stringpair;
+
+LdifReader::LdifReader( std::istream &input )
+ : m_ldifstream(input), m_lineNumber(0)
{
DEBUG(LDAP_DEBUG_TRACE, "<> LdifReader::LdifReader()" << std::endl);
+ this->m_version = 0;
+ // read the first record to find out version and type of the LDIF
+ this->readNextRecord(true);
+ this->m_currentIsFirst = true;
}
-int LdifReader::readNextRecord()
+int LdifReader::readNextRecord( bool first )
{
DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::readRecord()" << std::endl);
std::string line;
std::string value;
int numLine = 0;
int recordType = 0;
+
+ if ( (! first) && this->m_currentIsFirst == true )
+ {
+ this->m_currentIsFirst = false;
+ return m_curRecType;
+ }
+
m_currentRecord.clear();
- while ( !this->getLdifLine(line) && line != "" )
+ while ( !this->getLdifLine(line) )
{
DEBUG(LDAP_DEBUG_TRACE, " Line: " << line << std::endl );
- int rc = this->splitLine(line, type, value);
- if ( rc )
+
+ // skip comments and empty lines between entries
+ if ( line[0] == '#' || ( numLine == 0 && line.size() == 0 ) )
+ {
+ DEBUG(LDAP_DEBUG_TRACE, "skipping empty line or comment" << std::endl );
+ continue;
+ }
+ if ( line.size() == 0 )
{
- DEBUG(LDAP_DEBUG_TRACE, " Error while splitting ldif line"
- << std::endl);
+ // End of Entry
+ break;
}
+
+ this->splitLine(line, type, value);
+
if ( numLine == 0 )
{
+ if ( type == "version" )
+ {
+ std::istringstream valuestream(value);
+ valuestream >> this->m_version;
+ if ( this->m_version != 1 ) // there is no other Version than LDIFv1
+ {
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Unsuported LDIF Version";
+ throw( std::runtime_error(err.str()) );
+ }
+ continue;
+ }
if ( type == "dn" ) // Record should start with the DN ...
{
DEBUG(LDAP_DEBUG_TRACE, " Record DN:" << value << std::endl);
else if ( type == "include" ) // ... or it might be an "include" line
{
DEBUG(LDAP_DEBUG_TRACE, " Include directive: " << value << std::endl);
- //this->readIncludeLine(value);
+ if ( this->m_version == 1 )
+ {
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": \"include\" not allowed in LDIF version 1.";
+ throw( std::runtime_error(err.str()) );
+ }
+ else
+ {
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": \"include\" not yet suppported.";
+ throw( std::runtime_error(err.str()) );
+ }
}
else
{
DEBUG(LDAP_DEBUG_TRACE, " Record doesn't start with a DN"
<< std::endl);
- return 0;
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": LDIF record does not start with a DN.";
+ throw( std::runtime_error(err.str()) );
}
}
if ( numLine == 1 ) // might contain "changtype" to indicate a change request
{
if ( type == "changetype" )
{
+ if ( first )
+ {
+ this->m_ldifTypeRequest = true;
+ }
+ else if (! this->m_ldifTypeRequest )
+ {
+ // Change Request in Entry record LDIF, should we accept it?
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Change Request in an entry-only LDIF.";
+ throw( std::runtime_error(err.str()) );
+ }
if ( value == "modify" )
{
recordType = LDAPMsg::MODIFY_REQUEST;
}
else
{
- DEBUG(LDAP_DEBUG_TRACE, " Unknown change request <" << value << ">" << std::endl);
- return 0;
+ DEBUG(LDAP_DEBUG_TRACE, " Unknown change request <"
+ << value << ">" << std::endl);
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Unknown changetype: \"" << value << "\".";
+ throw( std::runtime_error(err.str()) );
}
}
else
{
+ if ( first )
+ {
+ this->m_ldifTypeRequest = false;
+ }
+ else if (this->m_ldifTypeRequest )
+ {
+ // Entry record in Change record LDIF, should we accept
+ // it (e.g. as AddRequest)?
+ }
recordType = LDAPMsg::SEARCH_ENTRY;
}
}
- m_currentRecord.push_back(std::pair<std::string, std::string>(type, value));
+ m_currentRecord.push_back( stringpair(type, value) );
numLine++;
}
- DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::readRecord()" << std::endl);
+ DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::readRecord() return: "
+ << recordType << std::endl);
m_curRecType = recordType;
return recordType;
}
LDAPEntry LdifReader::getEntryRecord()
{
+ std::list<stringpair>::const_iterator i = m_currentRecord.begin();
if ( m_curRecType != LDAPMsg::SEARCH_ENTRY )
{
- // Error
+ throw( std::runtime_error( "The LDIF record: '" + i->second +
+ "' is not a valid LDAP Entry" ));
}
- std::list<std::pair<std::string, std::string> >::const_iterator i = m_currentRecord.begin();
LDAPEntry resEntry(i->second);
i++;
LDAPAttribute curAttr(i->first);
}
else
{
- if ( curAl->getAttributeByName( i->first ) )
- // Attribute exists already -> Syntax Error
+ const LDAPAttribute* existing = curAl->getAttributeByName( i->first );
+ if ( existing )
{
- // Error
+ // Attribute exists already (handle gracefully)
+ curAl->addAttribute( curAttr );
+ curAttr = LDAPAttribute( *existing );
+ curAttr.addValue(i->second);
+ curAl->delAttribute( i->first );
}
else
{
{
DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::getLdifLine()" << std::endl);
+ this->m_lineNumber++;
if ( ! getline(m_ldifstream, ldifline) )
{
return -1;
}
-
while ( m_ldifstream &&
(m_ldifstream.peek() == ' ' || m_ldifstream.peek() == '\t'))
{
m_ldifstream.ignore();
getline(m_ldifstream, cat);
ldifline += cat;
+ this->m_lineNumber++;
}
DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::getLdifLine()" << std::endl);
return 0;
}
-int LdifReader::splitLine(const std::string& line,
+void LdifReader::splitLine(
+ const std::string& line,
std::string &type,
- std::string &value)
+ std::string &value) const
{
std::string::size_type pos = line.find(':');
if ( pos == std::string::npos )
{
- DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. Not `:` separator"
+ DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. No `:` separator"
<< std::endl );
- return -1;
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber << ": Invalid LDIF line. No `:` separator";
+ throw( std::runtime_error( err.str() ));
}
+
type = line.substr(0, pos);
if ( pos == line.size() )
{
// empty value
value = "";
- return 0;
+ return;
}
+
pos++;
char delim = line[pos];
if ( delim == ':' || delim == '<' )
{
pos++;
}
+
for( ; pos < line.size() && isspace(line[pos]); pos++ )
{ /* empty */ }
{
value = "";
DEBUG( LDAP_DEBUG_TRACE, " invalid base64 content" << std::endl );
- return -1;
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber << ": Can't decode Base64 data";
+ throw( std::runtime_error( err.str() ));
}
else if ( rc == SASL_BUFOVER )
{
value = "";
DEBUG( LDAP_DEBUG_TRACE, " not enough space in output buffer"
<< std::endl );
- return -1;
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Can't decode Base64 data. Buffer too small";
+ throw( std::runtime_error( err.str() ));
}
}
else if ( delim == '<' )
{
// URL value
DEBUG(LDAP_DEBUG_TRACE, " url value" << std::endl );
- return -1;
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": URLs are currently not supported";
+ throw( std::runtime_error( err.str() ));
}
else
{
}
DEBUG(LDAP_DEBUG_TRACE, " Type: <" << type << ">" << std::endl );
DEBUG(LDAP_DEBUG_TRACE, " Value: <" << value << ">" << std::endl );
- return 0;
+ return;
}
std::string LdifReader::readIncludeLine( const std::string& line ) const