]> git.sur5r.net Git - openldap/blob - contrib/ldapc++/src/LdifReader.cpp
Happy New Year
[openldap] / contrib / ldapc++ / src / LdifReader.cpp
1 // $OpenLDAP$
2 /*
3  * Copyright 2008-2018 The 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                         << ": Unsupported 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 supported.";
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 }