3 * Copyright 2008-2011 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
7 #include "LdifReader.h"
8 #include "LDAPMessage.h"
10 #include "LDAPAttributeList.h"
11 #include "LDAPAttribute.h"
19 #include <sasl/saslutil.h> // For base64 routines
21 typedef std::pair<std::string, std::string> stringpair;
23 LdifReader::LdifReader( std::istream &input )
24 : m_ldifstream(input), m_lineNumber(0)
26 DEBUG(LDAP_DEBUG_TRACE, "<> LdifReader::LdifReader()" << std::endl);
28 // read the first record to find out version and type of the LDIF
29 this->readNextRecord(true);
30 this->m_currentIsFirst = true;
33 int LdifReader::readNextRecord( bool first )
35 DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::readRecord()" << std::endl);
42 if ( (! first) && this->m_currentIsFirst == true )
44 this->m_currentIsFirst = false;
48 m_currentRecord.clear();
50 while ( !this->getLdifLine(line) )
52 DEBUG(LDAP_DEBUG_TRACE, " Line: " << line << std::endl );
54 // skip comments and empty lines between entries
55 if ( line[0] == '#' || ( numLine == 0 && line.size() == 0 ) )
57 DEBUG(LDAP_DEBUG_TRACE, "skipping empty line or comment" << std::endl );
60 if ( line.size() == 0 )
66 this->splitLine(line, type, value);
70 if ( type == "version" )
72 std::istringstream valuestream(value);
73 valuestream >> this->m_version;
74 if ( this->m_version != 1 ) // there is no other Version than LDIFv1
76 std::ostringstream err;
77 err << "Line " << this->m_lineNumber
78 << ": Unsuported LDIF Version";
79 throw( std::runtime_error(err.str()) );
83 if ( type == "dn" ) // Record should start with the DN ...
85 DEBUG(LDAP_DEBUG_TRACE, " Record DN:" << value << std::endl);
87 else if ( type == "include" ) // ... or it might be an "include" line
89 DEBUG(LDAP_DEBUG_TRACE, " Include directive: " << value << std::endl);
90 if ( this->m_version == 1 )
92 std::ostringstream err;
93 err << "Line " << this->m_lineNumber
94 << ": \"include\" not allowed in LDIF version 1.";
95 throw( std::runtime_error(err.str()) );
99 std::ostringstream err;
100 err << "Line " << this->m_lineNumber
101 << ": \"include\" not yet suppported.";
102 throw( std::runtime_error(err.str()) );
107 DEBUG(LDAP_DEBUG_TRACE, " Record doesn't start with a DN"
109 std::ostringstream err;
110 err << "Line " << this->m_lineNumber
111 << ": LDIF record does not start with a DN.";
112 throw( std::runtime_error(err.str()) );
115 if ( numLine == 1 ) // might contain "changtype" to indicate a change request
117 if ( type == "changetype" )
121 this->m_ldifTypeRequest = true;
123 else if (! this->m_ldifTypeRequest )
125 // Change Request in Entry record LDIF, should we accept it?
126 std::ostringstream err;
127 err << "Line " << this->m_lineNumber
128 << ": Change Request in an entry-only LDIF.";
129 throw( std::runtime_error(err.str()) );
131 if ( value == "modify" )
133 recordType = LDAPMsg::MODIFY_REQUEST;
135 else if ( value == "add" )
137 recordType = LDAPMsg::ADD_REQUEST;
139 else if ( value == "delete" )
141 recordType = LDAPMsg::DELETE_REQUEST;
143 else if ( value == "modrdn" )
145 recordType = LDAPMsg::MODRDN_REQUEST;
149 DEBUG(LDAP_DEBUG_TRACE, " Unknown change request <"
150 << value << ">" << std::endl);
151 std::ostringstream err;
152 err << "Line " << this->m_lineNumber
153 << ": Unknown changetype: \"" << value << "\".";
154 throw( std::runtime_error(err.str()) );
161 this->m_ldifTypeRequest = false;
163 else if (this->m_ldifTypeRequest )
165 // Entry record in Change record LDIF, should we accept
166 // it (e.g. as AddRequest)?
168 recordType = LDAPMsg::SEARCH_ENTRY;
171 m_currentRecord.push_back( stringpair(type, value) );
174 DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::readRecord() return: "
175 << recordType << std::endl);
176 m_curRecType = recordType;
180 LDAPEntry LdifReader::getEntryRecord()
182 std::list<stringpair>::const_iterator i = m_currentRecord.begin();
183 if ( m_curRecType != LDAPMsg::SEARCH_ENTRY )
185 throw( std::runtime_error( "The LDIF record: '" + i->second +
186 "' is not a valid LDAP Entry" ));
188 LDAPEntry resEntry(i->second);
190 LDAPAttribute curAttr(i->first);
191 LDAPAttributeList *curAl = new LDAPAttributeList();
192 for ( ; i != m_currentRecord.end(); i++ )
194 if ( i->first == curAttr.getName() )
196 curAttr.addValue(i->second);
200 const LDAPAttribute* existing = curAl->getAttributeByName( i->first );
203 // Attribute exists already (handle gracefully)
204 curAl->addAttribute( curAttr );
205 curAttr = LDAPAttribute( *existing );
206 curAttr.addValue(i->second);
207 curAl->delAttribute( i->first );
211 curAl->addAttribute( curAttr );
212 curAttr = LDAPAttribute( i->first, i->second );
216 curAl->addAttribute( curAttr );
217 resEntry.setAttributes( curAl );
221 int LdifReader::getLdifLine(std::string &ldifline)
223 DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::getLdifLine()" << std::endl);
225 this->m_lineNumber++;
226 if ( ! getline(m_ldifstream, ldifline) )
230 while ( m_ldifstream &&
231 (m_ldifstream.peek() == ' ' || m_ldifstream.peek() == '\t'))
234 m_ldifstream.ignore();
235 getline(m_ldifstream, cat);
237 this->m_lineNumber++;
240 DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::getLdifLine()" << std::endl);
244 void LdifReader::splitLine(
245 const std::string& line,
247 std::string &value) const
249 std::string::size_type pos = line.find(':');
250 if ( pos == std::string::npos )
252 DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. No `:` separator"
254 std::ostringstream err;
255 err << "Line " << this->m_lineNumber << ": Invalid LDIF line. No `:` separator";
256 throw( std::runtime_error( err.str() ));
259 type = line.substr(0, pos);
260 if ( pos == line.size() )
268 char delim = line[pos];
269 if ( delim == ':' || delim == '<' )
274 for( ; pos < line.size() && isspace(line[pos]); pos++ )
277 value = line.substr(pos);
281 // Base64 encoded value
282 DEBUG(LDAP_DEBUG_TRACE, " base64 encoded value" << std::endl );
283 char outbuf[value.size()];
284 int rc = sasl_decode64(value.c_str(), value.size(),
285 outbuf, value.size(), NULL);
288 value = std::string(outbuf);
290 else if ( rc == SASL_BADPROT )
293 DEBUG( LDAP_DEBUG_TRACE, " invalid base64 content" << std::endl );
294 std::ostringstream err;
295 err << "Line " << this->m_lineNumber << ": Can't decode Base64 data";
296 throw( std::runtime_error( err.str() ));
298 else if ( rc == SASL_BUFOVER )
301 DEBUG( LDAP_DEBUG_TRACE, " not enough space in output buffer"
303 std::ostringstream err;
304 err << "Line " << this->m_lineNumber
305 << ": Can't decode Base64 data. Buffer too small";
306 throw( std::runtime_error( err.str() ));
309 else if ( delim == '<' )
312 DEBUG(LDAP_DEBUG_TRACE, " url value" << std::endl );
313 std::ostringstream err;
314 err << "Line " << this->m_lineNumber
315 << ": URLs are currently not supported";
316 throw( std::runtime_error( err.str() ));
321 DEBUG(LDAP_DEBUG_TRACE, " string value" << std::endl );
323 DEBUG(LDAP_DEBUG_TRACE, " Type: <" << type << ">" << std::endl );
324 DEBUG(LDAP_DEBUG_TRACE, " Value: <" << value << ">" << std::endl );
328 std::string LdifReader::readIncludeLine( const std::string& line ) const
330 std::string::size_type pos = sizeof("file:") - 1;
331 std::string scheme = line.substr( 0, pos );
334 // only file:// URLs supported currently
335 if ( scheme != "file:" )
337 DEBUG( LDAP_DEBUG_TRACE, "unsupported scheme: " << scheme
340 else if ( line[pos] == '/' )
342 if ( line[pos+1] == '/' )
346 file = line.substr(pos, std::string::npos);
347 DEBUG( LDAP_DEBUG_TRACE, "target file: " << file << std::endl);