1 // OpenLDAP: pkg/ldap/contrib/ldapc++/src/LdifReader.cpp,v 1.4.2.5 2009/09/29 21:35:03 quanah Exp 2 /* 3 * Copyright 2008, OpenLDAP Foundation, All Rights Reserved. 4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file 5 */ 6 7 #include "LdifReader.h" 8 #include "LDAPMessage.h" 9 #include "LDAPEntry.h" 10 #include "LDAPAttributeList.h" 11 #include "LDAPAttribute.h" 12 #include "LDAPUrl.h" 13 #include "debug.h" 14 15 #include <string> 16 #include <sstream> 17 #include <stdexcept> 18 19 #include <sasl/saslutil.h> // For base64 routines 20 21 typedef std::pair<std::string, std::string> stringpair; 22 23 LdifReader::LdifReader( std::istream &input ) 24 : m_ldifstream(input), m_lineNumber(0) 25 { 26 DEBUG(LDAP_DEBUG_TRACE, "<> LdifReader::LdifReader()" << std::endl); 27 this->m_version = 0; 28 // read the first record to find out version and type of the LDIF 29 this->readNextRecord(true); 30 this->m_currentIsFirst = true; 31 } 32 33 int LdifReader::readNextRecord( bool first ) 34 { 35 DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::readRecord()" << std::endl); 36 std::string line; 37 std::string type; 38 std::string value; 39 int numLine = 0; 40 int recordType = 0; 41 42 if ( (! first) && this->m_currentIsFirst == true ) 43 { 44 this->m_currentIsFirst = false; 45 return m_curRecType; 46 } 47 48 m_currentRecord.clear(); 49 50 while ( !this->getLdifLine(line) ) 51 { 52 DEBUG(LDAP_DEBUG_TRACE, " Line: " << line << std::endl ); 53 54 // skip comments and empty lines between entries 55 if ( line[0] == '#' || ( numLine == 0 && line.size() == 0 ) ) 56 { 57 DEBUG(LDAP_DEBUG_TRACE, "skipping empty line or comment" << std::endl ); 58 continue; 59 } 60 if ( line.size() == 0 ) 61 { 62 // End of Entry 63 break; 64 } 65 66 this->splitLine(line, type, value); 67 68 if ( numLine == 0 ) 69 { 70 if ( type == "version" ) 71 { 72 std::istringstream valuestream(value); 73 valuestream >> this->m_version; 74 if ( this->m_version != 1 ) // there is no other Version than LDIFv1 75 { 76 std::ostringstream err; 77 err << "Line " << this->m_lineNumber 78 << ": Unsuported LDIF Version"; 79 throw( std::runtime_error(err.str()) ); 80 } 81 continue; 82 } 83 if ( type == "dn" ) // Record should start with the DN ... 84 { 85 DEBUG(LDAP_DEBUG_TRACE, " Record DN:" << value << std::endl); 86 } 87 else if ( type == "include" ) // ... or it might be an "include" line 88 { 89 DEBUG(LDAP_DEBUG_TRACE, " Include directive: " << value << std::endl); 90 if ( this->m_version == 1 ) 91 { 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()) ); 96 } 97 else 98 { 99 std::ostringstream err; 100 err << "Line " << this->m_lineNumber 101 << ": \"include\" not yet suppported."; 102 throw( std::runtime_error(err.str()) ); 103 } 104 } 105 else 106 { 107 DEBUG(LDAP_DEBUG_TRACE, " Record doesn't start with a DN" 108 << std::endl); 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()) ); 113 } 114 } 115 if ( numLine == 1 ) // might contain "changtype" to indicate a change request 116 { 117 if ( type == "changetype" ) 118 { 119 if ( first ) 120 { 121 this->m_ldifTypeRequest = true; 122 } 123 else if (! this->m_ldifTypeRequest ) 124 { 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()) ); 130 } 131 if ( value == "modify" ) 132 { 133 recordType = LDAPMsg::MODIFY_REQUEST; 134 } 135 else if ( value == "add" ) 136 { 137 recordType = LDAPMsg::ADD_REQUEST; 138 } 139 else if ( value == "delete" ) 140 { 141 recordType = LDAPMsg::DELETE_REQUEST; 142 } 143 else if ( value == "modrdn" ) 144 { 145 recordType = LDAPMsg::MODRDN_REQUEST; 146 } 147 else 148 { 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()) ); 155 } 156 } 157 else 158 { 159 if ( first ) 160 { 161 this->m_ldifTypeRequest = false; 162 } 163 else if (this->m_ldifTypeRequest ) 164 { 165 // Entry record in Change record LDIF, should we accept 166 // it (e.g. as AddRequest)? 167 } 168 recordType = LDAPMsg::SEARCH_ENTRY; 169 } 170 } 171 m_currentRecord.push_back( stringpair(type, value) ); 172 numLine++; 173 } 174 DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::readRecord() return: " 175 << recordType << std::endl); 176 m_curRecType = recordType; 177 return recordType; 178 } 179 180 LDAPEntry LdifReader::getEntryRecord() 181 { 182 std::list<stringpair>::const_iterator i = m_currentRecord.begin(); 183 if ( m_curRecType != LDAPMsg::SEARCH_ENTRY ) 184 { 185 throw( std::runtime_error( "The LDIF record: '" + i->second + 186 "' is not a valid LDAP Entry" )); 187 } 188 LDAPEntry resEntry(i->second); 189 i++; 190 LDAPAttribute curAttr(i->first); 191 LDAPAttributeList *curAl = new LDAPAttributeList(); 192 for ( ; i != m_currentRecord.end(); i++ ) 193 { 194 if ( i->first == curAttr.getName() ) 195 { 196 curAttr.addValue(i->second); 197 } 198 else 199 { 200 const LDAPAttribute* existing = curAl->getAttributeByName( i->first ); 201 if ( existing ) 202 { 203 // Attribute exists already (handle gracefully) 204 curAl->addAttribute( curAttr ); 205 curAttr = LDAPAttribute( *existing ); 206 curAttr.addValue(i->second); 207 curAl->delAttribute( i->first ); 208 } 209 else 210 { 211 curAl->addAttribute( curAttr ); 212 curAttr = LDAPAttribute( i->first, i->second ); 213 } 214 } 215 } 216 curAl->addAttribute( curAttr ); 217 resEntry.setAttributes( curAl ); 218 return resEntry; 219 } 220 221 int LdifReader::getLdifLine(std::string &ldifline) 222 { 223 DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::getLdifLine()" << std::endl); 224 225 this->m_lineNumber++; 226 if ( ! getline(m_ldifstream, ldifline) ) 227 { 228 return -1; 229 } 230 while ( m_ldifstream && 231 (m_ldifstream.peek() == ' ' || m_ldifstream.peek() == '\t')) 232 { 233 std::string cat; 234 m_ldifstream.ignore(); 235 getline(m_ldifstream, cat); 236 ldifline += cat; 237 this->m_lineNumber++; 238 } 239 240 DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::getLdifLine()" << std::endl); 241 return 0; 242 } 243 244 void LdifReader::splitLine( 245 const std::string& line, 246 std::string &type, 247 std::string &value) const 248 { 249 std::string::size_type pos = line.find(':'); 250 if ( pos == std::string::npos ) 251 { 252 DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. No `:` separator" 253 << std::endl ); 254 std::ostringstream err; 255 err << "Line " << this->m_lineNumber << ": Invalid LDIF line. No `:` separator"; 256 throw( std::runtime_error( err.str() )); 257 } 258 259 type = line.substr(0, pos); 260 if ( pos == line.size() ) 261 { 262 // empty value 263 value = ""; 264 return; 265 } 266 267 pos++; 268 char delim = line[pos]; 269 if ( delim == ':' || delim == '<' ) 270 { 271 pos++; 272 } 273 274 for( ; pos < line.size() && isspace(line[pos]); pos++ ) 275 { /* empty */ } 276 277 value = line.substr(pos); 278 279 if ( delim == ':' ) 280 { 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); 286 if( rc == SASL_OK ) 287 { 288 value = std::string(outbuf); 289 } 290 else if ( rc == SASL_BADPROT ) 291 { 292 value = ""; 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() )); 297 } 298 else if ( rc == SASL_BUFOVER ) 299 { 300 value = ""; 301 DEBUG( LDAP_DEBUG_TRACE, " not enough space in output buffer" 302 << std::endl ); 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() )); 307 } 308 } 309 else if ( delim == '<' ) 310 { 311 // URL value 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() )); 317 } 318 else 319 { 320 // "normal" value 321 DEBUG(LDAP_DEBUG_TRACE, " string value" << std::endl ); 322 } 323 DEBUG(LDAP_DEBUG_TRACE, " Type: <" << type << ">" << std::endl ); 324 DEBUG(LDAP_DEBUG_TRACE, " Value: <" << value << ">" << std::endl ); 325 return; 326 } 327 328 std::string LdifReader::readIncludeLine( const std::string& line ) const 329 { 330 std::string::size_type pos = sizeof("file:") - 1; 331 std::string scheme = line.substr( 0, pos ); 332 std::string file; 333 334 // only file:// URLs supported currently 335 if ( scheme != "file:" ) 336 { 337 DEBUG( LDAP_DEBUG_TRACE, "unsupported scheme: " << scheme 338 << std::endl); 339 } 340 else if ( line[pos] == '/' ) 341 { 342 if ( line[pos+1] == '/' ) 343 { 344 pos += 2; 345 } 346 file = line.substr(pos, std::string::npos); 347 DEBUG( LDAP_DEBUG_TRACE, "target file: " << file << std::endl); 348 } 349 return file; 350 } 351