summaryrefslogtreecommitdiffstats
path: root/contrib/ldapc++/src/LdifReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ldapc++/src/LdifReader.cpp')
-rw-r--r--contrib/ldapc++/src/LdifReader.cpp350
1 files changed, 350 insertions, 0 deletions
diff --git a/contrib/ldapc++/src/LdifReader.cpp b/contrib/ldapc++/src/LdifReader.cpp
new file mode 100644
index 0000000..207806d
--- /dev/null
+++ b/contrib/ldapc++/src/LdifReader.cpp
@@ -0,0 +1,350 @@
+// $OpenLDAP$
+/*
+ * Copyright 2008-2022 The 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>
+#include <sstream>
+#include <stdexcept>
+
+#include <sasl/saslutil.h> // For base64 routines
+
+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( bool first )
+{
+ DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::readRecord()" << std::endl);
+ std::string line;
+ std::string type;
+ 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) )
+ {
+ DEBUG(LDAP_DEBUG_TRACE, " Line: " << line << std::endl );
+
+ // 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;
+ }
+
+ 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
+ << ": Unsupported 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);
+ 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 supported.";
+ throw( std::runtime_error(err.str()) );
+ }
+ }
+ else
+ {
+ DEBUG(LDAP_DEBUG_TRACE, " Record doesn't start with a DN"
+ << std::endl);
+ 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 if ( value == "add" )
+ {
+ recordType = LDAPMsg::ADD_REQUEST;
+ }
+ else if ( value == "delete" )
+ {
+ recordType = LDAPMsg::DELETE_REQUEST;
+ }
+ else if ( value == "modrdn" )
+ {
+ recordType = LDAPMsg::MODRDN_REQUEST;
+ }
+ else
+ {
+ 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( stringpair(type, value) );
+ numLine++;
+ }
+ 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 )
+ {
+ throw( std::runtime_error( "The LDIF record: '" + i->second +
+ "' is not a valid LDAP Entry" ));
+ }
+ 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
+ {
+ const LDAPAttribute* existing = curAl->getAttributeByName( i->first );
+ if ( existing )
+ {
+ // Attribute exists already (handle gracefully)
+ curAl->addAttribute( curAttr );
+ curAttr = LDAPAttribute( *existing );
+ curAttr.addValue(i->second);
+ curAl->delAttribute( i->first );
+ }
+ 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);
+
+ this->m_lineNumber++;
+ 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;
+ this->m_lineNumber++;
+ }
+
+ DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::getLdifLine()" << std::endl);
+ return 0;
+}
+
+void LdifReader::splitLine(
+ const std::string& line,
+ std::string &type,
+ std::string &value) const
+{
+ std::string::size_type pos = line.find(':');
+ if ( pos == std::string::npos )
+ {
+ DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. No `:` separator"
+ << std::endl );
+ 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;
+ }
+
+ pos++;
+ char delim = line[pos];
+ if ( delim == ':' || delim == '<' )
+ {
+ pos++;
+ }
+
+ 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 );
+ 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 );
+ 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 );
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": URLs are currently not supported";
+ throw( std::runtime_error( err.str() ));
+ }
+ 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 );
+ return;
+}
+
+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;
+}