]> git.sur5r.net Git - openldap/blobdiff - contrib/ldapc++/src/LdifReader.cpp
Merge remote-tracking branch 'origin/mdb.master' into OPENLDAP_REL_ENG_2_5
[openldap] / contrib / ldapc++ / src / LdifReader.cpp
index 1ccd43b676e8ca0b775182810a2b5b8690ec52f5..9db51e83411f93e70931a79792cfaad1eef911e6 100644 (file)
@@ -1,5 +1,6 @@
+// $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;
@@ -28,19 +38,48 @@ int LdifReader::readNextRecord()
     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);
@@ -48,19 +87,47 @@ int LdifReader::readNextRecord()
             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;
@@ -79,30 +146,45 @@ int LdifReader::readNextRecord()
                 }
                 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);
@@ -115,10 +197,14 @@ LDAPEntry LdifReader::getEntryRecord()
         }
         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
             {
@@ -136,11 +222,11 @@ 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'))
     {
@@ -148,36 +234,43 @@ int LdifReader::getLdifLine(std::string &ldifline)
         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 */ }
 
@@ -198,21 +291,29 @@ int LdifReader::splitLine(const std::string& line,
         {
             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 
     {
@@ -221,7 +322,7 @@ int LdifReader::splitLine(const std::string& line,
     }
     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