Back to index

openldap  2.4.31
LdifReader.cpp
Go to the documentation of this file.
00001 // $OpenLDAP$
00002 /*
00003  * Copyright 2008-2012 The OpenLDAP Foundation, All Rights Reserved.
00004  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
00005  */
00006 
00007 #include "LdifReader.h"
00008 #include "LDAPMessage.h"
00009 #include "LDAPEntry.h"
00010 #include "LDAPAttributeList.h"
00011 #include "LDAPAttribute.h"
00012 #include "LDAPUrl.h"
00013 #include "debug.h"
00014 
00015 #include <string>
00016 #include <sstream>
00017 #include <stdexcept>
00018 
00019 #include <sasl/saslutil.h> // For base64 routines
00020 
00021 typedef std::pair<std::string, std::string> stringpair;
00022 
00023 LdifReader::LdifReader( std::istream &input ) 
00024         : m_ldifstream(input), m_lineNumber(0)
00025 {
00026     DEBUG(LDAP_DEBUG_TRACE, "<> LdifReader::LdifReader()" << std::endl);
00027     this->m_version = 0;
00028     // read the first record to find out version and type of the LDIF
00029     this->readNextRecord(true);
00030     this->m_currentIsFirst = true;
00031 }
00032 
00033 int LdifReader::readNextRecord( bool first )
00034 {
00035     DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::readRecord()" << std::endl);
00036     std::string line;
00037     std::string type;
00038     std::string value;
00039     int numLine = 0;
00040     int recordType = 0;
00041 
00042     if ( (! first) && this->m_currentIsFirst == true )
00043     {
00044         this->m_currentIsFirst = false;
00045         return m_curRecType;
00046     }
00047 
00048     m_currentRecord.clear();
00049 
00050     while ( !this->getLdifLine(line) )
00051     {
00052         DEBUG(LDAP_DEBUG_TRACE, "  Line: " << line << std::endl );
00053 
00054         // skip comments and empty lines between entries
00055         if ( line[0] == '#' || ( numLine == 0 && line.size() == 0 ) )
00056         {
00057             DEBUG(LDAP_DEBUG_TRACE, "skipping empty line or comment" << std::endl );
00058             continue;
00059         }
00060         if ( line.size() == 0 ) 
00061         {
00062             // End of Entry
00063             break;
00064         }
00065 
00066         this->splitLine(line, type, value);
00067 
00068         if ( numLine == 0 )
00069         {
00070             if ( type == "version" )
00071             {
00072                 std::istringstream valuestream(value);
00073                 valuestream >> this->m_version;
00074                 if ( this->m_version != 1 ) // there is no other Version than LDIFv1 
00075                 {
00076                     std::ostringstream err;
00077                     err << "Line " << this->m_lineNumber 
00078                         << ": Unsuported LDIF Version";
00079                     throw( std::runtime_error(err.str()) );
00080                 }
00081                 continue;
00082             }
00083             if ( type == "dn" ) // Record should start with the DN ...
00084             {
00085                 DEBUG(LDAP_DEBUG_TRACE, " Record DN:" << value << std::endl);
00086             }
00087             else if ( type == "include" ) // ... or it might be an "include" line
00088             {
00089                 DEBUG(LDAP_DEBUG_TRACE, " Include directive: " << value << std::endl);
00090                 if ( this->m_version == 1 )
00091                 {
00092                     std::ostringstream err;
00093                     err << "Line " << this->m_lineNumber 
00094                         << ": \"include\" not allowed in LDIF version 1.";
00095                     throw( std::runtime_error(err.str()) );
00096                 }
00097                 else
00098                 {
00099                     std::ostringstream err;
00100                     err << "Line " << this->m_lineNumber 
00101                         << ": \"include\" not yet suppported.";
00102                     throw( std::runtime_error(err.str()) );
00103                 }
00104             }
00105             else
00106             {
00107                 DEBUG(LDAP_DEBUG_TRACE, " Record doesn't start with a DN" 
00108                             << std::endl);
00109                 std::ostringstream err;
00110                 err << "Line " << this->m_lineNumber 
00111                     << ": LDIF record does not start with a DN.";
00112                 throw( std::runtime_error(err.str()) );
00113             }
00114         }
00115         if ( numLine == 1 ) // might contain "changtype" to indicate a change request
00116         {
00117             if ( type == "changetype" ) 
00118             {
00119                 if ( first ) 
00120                 {
00121                     this->m_ldifTypeRequest = true;
00122                 }
00123                 else if (! this->m_ldifTypeRequest )
00124                 {
00125                     // Change Request in Entry record LDIF, should we accept it?
00126                     std::ostringstream err;
00127                     err << "Line " << this->m_lineNumber 
00128                         << ": Change Request in an entry-only LDIF.";
00129                     throw( std::runtime_error(err.str()) );
00130                 }
00131                 if ( value == "modify" )
00132                 {
00133                     recordType = LDAPMsg::MODIFY_REQUEST;
00134                 }
00135                 else if ( value == "add" )
00136                 {
00137                     recordType = LDAPMsg::ADD_REQUEST;
00138                 }
00139                 else if ( value == "delete" )
00140                 {
00141                     recordType = LDAPMsg::DELETE_REQUEST;
00142                 }
00143                 else if ( value == "modrdn" )
00144                 {   
00145                     recordType = LDAPMsg::MODRDN_REQUEST;
00146                 }
00147                 else
00148                 {
00149                     DEBUG(LDAP_DEBUG_TRACE, " Unknown change request <" 
00150                             << value << ">" << std::endl);
00151                     std::ostringstream err;
00152                     err << "Line " << this->m_lineNumber 
00153                         << ": Unknown changetype: \"" << value << "\".";
00154                     throw( std::runtime_error(err.str()) );
00155                 }
00156             }
00157             else
00158             {
00159                 if ( first ) 
00160                 {
00161                     this->m_ldifTypeRequest = false;
00162                 }
00163                 else if (this->m_ldifTypeRequest )
00164                 {
00165                     // Entry record in Change record LDIF, should we accept 
00166                     // it (e.g. as AddRequest)?
00167                 }
00168                 recordType = LDAPMsg::SEARCH_ENTRY;
00169             }
00170         }
00171         m_currentRecord.push_back( stringpair(type, value) );
00172         numLine++;
00173     }
00174     DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::readRecord() return: " 
00175             << recordType << std::endl);
00176     m_curRecType = recordType;
00177     return recordType;
00178 }
00179 
00180 LDAPEntry LdifReader::getEntryRecord()
00181 {
00182     std::list<stringpair>::const_iterator i = m_currentRecord.begin();
00183     if ( m_curRecType != LDAPMsg::SEARCH_ENTRY )
00184     {
00185         throw( std::runtime_error( "The LDIF record: '" + i->second +
00186                                    "' is not a valid LDAP Entry" ));
00187     }
00188     LDAPEntry resEntry(i->second);
00189     i++;
00190     LDAPAttribute curAttr(i->first);
00191     LDAPAttributeList *curAl = new LDAPAttributeList();
00192     for ( ; i != m_currentRecord.end(); i++ )
00193     {
00194         if ( i->first == curAttr.getName() )
00195         {
00196             curAttr.addValue(i->second);
00197         }
00198         else
00199         {
00200             const LDAPAttribute* existing = curAl->getAttributeByName( i->first );
00201             if ( existing )
00202             {
00203                 // Attribute exists already (handle gracefully)
00204                 curAl->addAttribute( curAttr );
00205                 curAttr = LDAPAttribute( *existing );
00206                 curAttr.addValue(i->second);
00207                 curAl->delAttribute( i->first );
00208             }
00209             else
00210             {
00211                 curAl->addAttribute( curAttr );
00212                 curAttr = LDAPAttribute( i->first, i->second );
00213             }
00214         }
00215     }
00216     curAl->addAttribute( curAttr );
00217     resEntry.setAttributes( curAl );
00218     return resEntry;
00219 }
00220 
00221 int LdifReader::getLdifLine(std::string &ldifline)
00222 {
00223     DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::getLdifLine()" << std::endl);
00224 
00225     this->m_lineNumber++;
00226     if ( ! getline(m_ldifstream, ldifline) )
00227     {
00228         return -1;
00229     }
00230     while ( m_ldifstream &&
00231         (m_ldifstream.peek() == ' ' || m_ldifstream.peek() == '\t'))
00232     {
00233         std::string cat;
00234         m_ldifstream.ignore();
00235         getline(m_ldifstream, cat);
00236         ldifline += cat;
00237         this->m_lineNumber++;
00238     }
00239 
00240     DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::getLdifLine()" << std::endl);
00241     return 0;
00242 }
00243 
00244 void LdifReader::splitLine(
00245             const std::string& line, 
00246             std::string &type,
00247             std::string &value) const
00248 {
00249     std::string::size_type pos = line.find(':');
00250     if ( pos == std::string::npos )
00251     {
00252         DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. No `:` separator" 
00253                 << std::endl );
00254         std::ostringstream err;
00255         err << "Line " << this->m_lineNumber << ": Invalid LDIF line. No `:` separator";
00256         throw( std::runtime_error( err.str() ));
00257     }
00258 
00259     type = line.substr(0, pos);
00260     if ( pos == line.size() )
00261     {
00262         // empty value
00263         value = "";
00264         return;
00265     }
00266 
00267     pos++;
00268     char delim = line[pos];
00269     if ( delim == ':' || delim == '<' )
00270     {
00271         pos++;
00272     }
00273 
00274     for( ; pos < line.size() && isspace(line[pos]); pos++ )
00275     { /* empty */ }
00276 
00277     value = line.substr(pos);
00278 
00279     if ( delim == ':' )
00280     {
00281         // Base64 encoded value
00282         DEBUG(LDAP_DEBUG_TRACE, "  base64 encoded value" << std::endl );
00283         char outbuf[value.size()];
00284         int rc = sasl_decode64(value.c_str(), value.size(), 
00285                 outbuf, value.size(), NULL);
00286         if( rc == SASL_OK )
00287         {
00288             value = std::string(outbuf);
00289         }
00290         else if ( rc == SASL_BADPROT )
00291         {
00292             value = "";
00293             DEBUG( LDAP_DEBUG_TRACE, " invalid base64 content" << std::endl );
00294             std::ostringstream err;
00295             err << "Line " << this->m_lineNumber << ": Can't decode Base64 data";
00296             throw( std::runtime_error( err.str() ));
00297         }
00298         else if ( rc == SASL_BUFOVER )
00299         {
00300             value = "";
00301             DEBUG( LDAP_DEBUG_TRACE, " not enough space in output buffer" 
00302                     << std::endl );
00303             std::ostringstream err;
00304             err << "Line " << this->m_lineNumber 
00305                 << ": Can't decode Base64 data. Buffer too small";
00306             throw( std::runtime_error( err.str() ));
00307         }
00308     }
00309     else if ( delim == '<' )
00310     {
00311         // URL value
00312         DEBUG(LDAP_DEBUG_TRACE, "  url value" << std::endl );
00313         std::ostringstream err;
00314         err << "Line " << this->m_lineNumber 
00315             << ": URLs are currently not supported";
00316         throw( std::runtime_error( err.str() ));
00317     }
00318     else 
00319     {
00320         // "normal" value
00321         DEBUG(LDAP_DEBUG_TRACE, "  string value" << std::endl );
00322     }
00323     DEBUG(LDAP_DEBUG_TRACE, "  Type: <" << type << ">" << std::endl );
00324     DEBUG(LDAP_DEBUG_TRACE, "  Value: <" << value << ">" << std::endl );
00325     return;
00326 }
00327 
00328 std::string LdifReader::readIncludeLine( const std::string& line ) const
00329 {
00330     std::string::size_type pos = sizeof("file:") - 1;
00331     std::string scheme = line.substr( 0, pos );
00332     std::string file;
00333 
00334     // only file:// URLs supported currently
00335     if ( scheme != "file:" )
00336     {
00337         DEBUG( LDAP_DEBUG_TRACE, "unsupported scheme: " << scheme 
00338                 << std::endl);
00339     }
00340     else if ( line[pos] == '/' )
00341     {
00342         if ( line[pos+1] == '/' )
00343         {
00344             pos += 2;
00345         }
00346         file = line.substr(pos, std::string::npos);
00347         DEBUG( LDAP_DEBUG_TRACE, "target file: " << file << std::endl);
00348     }
00349     return file;
00350 }