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