2008-03-28 07:05:10 -04:00
|
|
|
// $OpenLDAP$
|
2008-03-17 12:08:51 -04:00
|
|
|
/*
|
|
|
|
|
* Copyright 2008, OpenLDAP Foundation, All Rights Reserved.
|
|
|
|
|
* COPYING RESTRICTIONS APPLY, see COPYRIGHT file
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "LdifReader.h"
|
|
|
|
|
#include "LDAPMessage.h"
|
|
|
|
|
#include "LDAPEntry.h"
|
|
|
|
|
#include "LDAPAttributeList.h"
|
|
|
|
|
#include "LDAPAttribute.h"
|
|
|
|
|
#include "LDAPUrl.h"
|
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
|
|
#include <string>
|
2008-03-20 06:01:33 -04:00
|
|
|
#include <sstream>
|
2008-03-20 12:10:37 -04:00
|
|
|
#include <stdexcept>
|
2008-03-17 12:08:51 -04:00
|
|
|
|
|
|
|
|
#include <sasl/saslutil.h> // For base64 routines
|
|
|
|
|
|
2008-03-20 06:01:33 -04:00
|
|
|
typedef std::pair<std::string, std::string> stringpair;
|
2008-03-20 12:10:37 -04:00
|
|
|
|
|
|
|
|
LdifReader::LdifReader( std::istream &input )
|
|
|
|
|
: m_ldifstream(input), m_lineNumber(0)
|
2008-03-17 12:08:51 -04:00
|
|
|
{
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, "<> LdifReader::LdifReader()" << std::endl);
|
2008-03-20 06:01:33 -04:00
|
|
|
this->m_version = 0;
|
|
|
|
|
// read the first record to find out version and type of the LDIF
|
2008-03-20 12:10:37 -04:00
|
|
|
this->readNextRecord(true);
|
|
|
|
|
this->m_currentIsFirst = true;
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
|
2008-03-20 06:01:33 -04:00
|
|
|
int LdifReader::readNextRecord( bool first )
|
2008-03-17 12:08:51 -04:00
|
|
|
{
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::readRecord()" << std::endl);
|
|
|
|
|
std::string line;
|
|
|
|
|
std::string type;
|
|
|
|
|
std::string value;
|
|
|
|
|
int numLine = 0;
|
|
|
|
|
int recordType = 0;
|
2008-03-20 06:01:33 -04:00
|
|
|
|
|
|
|
|
if ( (! first) && this->m_currentIsFirst == true )
|
|
|
|
|
{
|
|
|
|
|
this->m_currentIsFirst = false;
|
|
|
|
|
return m_curRecType;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-17 12:08:51 -04:00
|
|
|
m_currentRecord.clear();
|
|
|
|
|
|
2008-03-20 06:01:33 -04:00
|
|
|
while ( !this->getLdifLine(line) )
|
2008-03-17 12:08:51 -04:00
|
|
|
{
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " Line: " << line << std::endl );
|
2008-03-20 06:01:33 -04:00
|
|
|
|
|
|
|
|
// 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 )
|
|
|
|
|
{
|
|
|
|
|
// End of Entry
|
|
|
|
|
break;
|
|
|
|
|
}
|
2008-03-20 12:10:37 -04:00
|
|
|
|
|
|
|
|
this->splitLine(line, type, value);
|
|
|
|
|
|
2008-03-17 12:08:51 -04:00
|
|
|
if ( numLine == 0 )
|
|
|
|
|
{
|
2008-03-20 06:01:33 -04:00
|
|
|
if ( type == "version" )
|
|
|
|
|
{
|
|
|
|
|
std::istringstream valuestream(value);
|
|
|
|
|
valuestream >> this->m_version;
|
|
|
|
|
if ( this->m_version != 1 ) // there is no other Version than LDIFv1
|
|
|
|
|
{
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber
|
|
|
|
|
<< ": Unsuported LDIF Version";
|
|
|
|
|
throw( std::runtime_error(err.str()) );
|
2008-03-20 06:01:33 -04:00
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2008-03-17 12:08:51 -04:00
|
|
|
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);
|
2008-03-20 06:01:33 -04:00
|
|
|
if ( this->m_version == 1 )
|
|
|
|
|
{
|
2008-03-20 12:10:37 -04:00
|
|
|
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()) );
|
2008-03-20 06:01:33 -04:00
|
|
|
}
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " Record doesn't start with a DN"
|
|
|
|
|
<< std::endl);
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber
|
|
|
|
|
<< ": LDIF record does not start with a DN.";
|
|
|
|
|
throw( std::runtime_error(err.str()) );
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( numLine == 1 ) // might contain "changtype" to indicate a change request
|
|
|
|
|
{
|
|
|
|
|
if ( type == "changetype" )
|
|
|
|
|
{
|
2008-03-20 06:01:33 -04:00
|
|
|
if ( first )
|
|
|
|
|
{
|
|
|
|
|
this->m_ldifTypeRequest = true;
|
|
|
|
|
}
|
|
|
|
|
else if (! this->m_ldifTypeRequest )
|
|
|
|
|
{
|
|
|
|
|
// Change Request in Entry record LDIF, should we accept it?
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber
|
|
|
|
|
<< ": Change Request in an entry-only LDIF.";
|
|
|
|
|
throw( std::runtime_error(err.str()) );
|
2008-03-20 06:01:33 -04:00
|
|
|
}
|
2008-03-17 12:08:51 -04:00
|
|
|
if ( value == "modify" )
|
|
|
|
|
{
|
|
|
|
|
recordType = LDAPMsg::MODIFY_REQUEST;
|
|
|
|
|
}
|
|
|
|
|
else if ( value == "add" )
|
|
|
|
|
{
|
|
|
|
|
recordType = LDAPMsg::ADD_REQUEST;
|
|
|
|
|
}
|
|
|
|
|
else if ( value == "delete" )
|
|
|
|
|
{
|
|
|
|
|
recordType = LDAPMsg::DELETE_REQUEST;
|
|
|
|
|
}
|
|
|
|
|
else if ( value == "modrdn" )
|
|
|
|
|
{
|
|
|
|
|
recordType = LDAPMsg::MODRDN_REQUEST;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-03-20 06:01:33 -04:00
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " Unknown change request <"
|
|
|
|
|
<< value << ">" << std::endl);
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber
|
|
|
|
|
<< ": Unknown changetype: \"" << value << "\".";
|
|
|
|
|
throw( std::runtime_error(err.str()) );
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-03-20 06:01:33 -04:00
|
|
|
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)?
|
|
|
|
|
}
|
2008-03-17 12:08:51 -04:00
|
|
|
recordType = LDAPMsg::SEARCH_ENTRY;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-03-20 06:01:33 -04:00
|
|
|
m_currentRecord.push_back( stringpair(type, value) );
|
2008-03-17 12:08:51 -04:00
|
|
|
numLine++;
|
|
|
|
|
}
|
2008-03-20 06:01:33 -04:00
|
|
|
DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::readRecord() return: "
|
|
|
|
|
<< recordType << std::endl);
|
2008-03-17 12:08:51 -04:00
|
|
|
m_curRecType = recordType;
|
|
|
|
|
return recordType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LDAPEntry LdifReader::getEntryRecord()
|
|
|
|
|
{
|
|
|
|
|
if ( m_curRecType != LDAPMsg::SEARCH_ENTRY )
|
|
|
|
|
{
|
|
|
|
|
// Error
|
|
|
|
|
}
|
2008-03-20 06:01:33 -04:00
|
|
|
std::list<stringpair>::const_iterator i = m_currentRecord.begin();
|
2008-03-17 12:08:51 -04:00
|
|
|
LDAPEntry resEntry(i->second);
|
|
|
|
|
i++;
|
|
|
|
|
LDAPAttribute curAttr(i->first);
|
|
|
|
|
LDAPAttributeList *curAl = new LDAPAttributeList();
|
|
|
|
|
for ( ; i != m_currentRecord.end(); i++ )
|
|
|
|
|
{
|
|
|
|
|
if ( i->first == curAttr.getName() )
|
|
|
|
|
{
|
|
|
|
|
curAttr.addValue(i->second);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-06-20 09:19:12 -04:00
|
|
|
const LDAPAttribute* existing = curAl->getAttributeByName( i->first );
|
|
|
|
|
if ( existing )
|
2008-03-17 12:08:51 -04:00
|
|
|
{
|
2008-06-20 09:19:12 -04:00
|
|
|
// Attribute exists already (handle gracefully)
|
|
|
|
|
curAl->addAttribute( curAttr );
|
|
|
|
|
curAttr = LDAPAttribute( *existing );
|
|
|
|
|
curAttr.addValue(i->second);
|
|
|
|
|
curAl->delAttribute( i->first );
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
curAl->addAttribute( curAttr );
|
|
|
|
|
curAttr = LDAPAttribute( i->first, i->second );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
curAl->addAttribute( curAttr );
|
|
|
|
|
resEntry.setAttributes( curAl );
|
|
|
|
|
return resEntry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int LdifReader::getLdifLine(std::string &ldifline)
|
|
|
|
|
{
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::getLdifLine()" << std::endl);
|
|
|
|
|
|
2008-03-20 12:10:37 -04:00
|
|
|
this->m_lineNumber++;
|
2008-03-17 12:08:51 -04:00
|
|
|
if ( ! getline(m_ldifstream, ldifline) )
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
while ( m_ldifstream &&
|
|
|
|
|
(m_ldifstream.peek() == ' ' || m_ldifstream.peek() == '\t'))
|
|
|
|
|
{
|
|
|
|
|
std::string cat;
|
|
|
|
|
m_ldifstream.ignore();
|
|
|
|
|
getline(m_ldifstream, cat);
|
|
|
|
|
ldifline += cat;
|
2008-03-20 12:10:37 -04:00
|
|
|
this->m_lineNumber++;
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::getLdifLine()" << std::endl);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-20 12:10:37 -04:00
|
|
|
void LdifReader::splitLine(
|
|
|
|
|
const std::string& line,
|
2008-03-17 12:08:51 -04:00
|
|
|
std::string &type,
|
2008-03-20 12:10:37 -04:00
|
|
|
std::string &value) const
|
2008-03-17 12:08:51 -04:00
|
|
|
{
|
|
|
|
|
std::string::size_type pos = line.find(':');
|
|
|
|
|
if ( pos == std::string::npos )
|
|
|
|
|
{
|
2008-03-20 12:10:37 -04:00
|
|
|
DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. No `:` separator"
|
2008-03-17 12:08:51 -04:00
|
|
|
<< std::endl );
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber << ": Invalid LDIF line. No `:` separator";
|
|
|
|
|
throw( std::runtime_error( err.str() ));
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
2008-03-20 12:10:37 -04:00
|
|
|
|
2008-03-17 12:08:51 -04:00
|
|
|
type = line.substr(0, pos);
|
|
|
|
|
if ( pos == line.size() )
|
|
|
|
|
{
|
|
|
|
|
// empty value
|
|
|
|
|
value = "";
|
2008-03-20 12:10:37 -04:00
|
|
|
return;
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
2008-03-20 12:10:37 -04:00
|
|
|
|
2008-03-17 12:08:51 -04:00
|
|
|
pos++;
|
|
|
|
|
char delim = line[pos];
|
|
|
|
|
if ( delim == ':' || delim == '<' )
|
|
|
|
|
{
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
2008-03-20 12:10:37 -04:00
|
|
|
|
2008-03-17 12:08:51 -04:00
|
|
|
for( ; pos < line.size() && isspace(line[pos]); pos++ )
|
|
|
|
|
{ /* empty */ }
|
|
|
|
|
|
|
|
|
|
value = line.substr(pos);
|
|
|
|
|
|
|
|
|
|
if ( delim == ':' )
|
|
|
|
|
{
|
|
|
|
|
// Base64 encoded value
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " base64 encoded value" << std::endl );
|
|
|
|
|
char outbuf[value.size()];
|
|
|
|
|
int rc = sasl_decode64(value.c_str(), value.size(),
|
|
|
|
|
outbuf, value.size(), NULL);
|
|
|
|
|
if( rc == SASL_OK )
|
|
|
|
|
{
|
|
|
|
|
value = std::string(outbuf);
|
|
|
|
|
}
|
|
|
|
|
else if ( rc == SASL_BADPROT )
|
|
|
|
|
{
|
|
|
|
|
value = "";
|
|
|
|
|
DEBUG( LDAP_DEBUG_TRACE, " invalid base64 content" << std::endl );
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber << ": Can't decode Base64 data";
|
|
|
|
|
throw( std::runtime_error( err.str() ));
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
else if ( rc == SASL_BUFOVER )
|
|
|
|
|
{
|
|
|
|
|
value = "";
|
|
|
|
|
DEBUG( LDAP_DEBUG_TRACE, " not enough space in output buffer"
|
|
|
|
|
<< std::endl );
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber
|
|
|
|
|
<< ": Can't decode Base64 data. Buffer too small";
|
|
|
|
|
throw( std::runtime_error( err.str() ));
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ( delim == '<' )
|
|
|
|
|
{
|
|
|
|
|
// URL value
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " url value" << std::endl );
|
2008-03-20 12:10:37 -04:00
|
|
|
std::ostringstream err;
|
|
|
|
|
err << "Line " << this->m_lineNumber
|
|
|
|
|
<< ": URLs are currently not supported";
|
|
|
|
|
throw( std::runtime_error( err.str() ));
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// "normal" value
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " string value" << std::endl );
|
|
|
|
|
}
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " Type: <" << type << ">" << std::endl );
|
|
|
|
|
DEBUG(LDAP_DEBUG_TRACE, " Value: <" << value << ">" << std::endl );
|
2008-03-20 12:10:37 -04:00
|
|
|
return;
|
2008-03-17 12:08:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string LdifReader::readIncludeLine( const std::string& line ) const
|
|
|
|
|
{
|
|
|
|
|
std::string::size_type pos = sizeof("file:") - 1;
|
|
|
|
|
std::string scheme = line.substr( 0, pos );
|
|
|
|
|
std::string file;
|
|
|
|
|
|
|
|
|
|
// only file:// URLs supported currently
|
|
|
|
|
if ( scheme != "file:" )
|
|
|
|
|
{
|
|
|
|
|
DEBUG( LDAP_DEBUG_TRACE, "unsupported scheme: " << scheme
|
|
|
|
|
<< std::endl);
|
|
|
|
|
}
|
|
|
|
|
else if ( line[pos] == '/' )
|
|
|
|
|
{
|
|
|
|
|
if ( line[pos+1] == '/' )
|
|
|
|
|
{
|
|
|
|
|
pos += 2;
|
|
|
|
|
}
|
|
|
|
|
file = line.substr(pos, std::string::npos);
|
|
|
|
|
DEBUG( LDAP_DEBUG_TRACE, "target file: " << file << std::endl);
|
|
|
|
|
}
|
|
|
|
|
return file;
|
|
|
|
|
}
|