1/*
2 This file is part of libkldap.
3 Copyright (c) 2004-2006 Szombathelyi György <gyurco@freemail.hu>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "ldif.h"
22
23#include <kdebug.h>
24
25using namespace KLDAP;
26
27class Ldif::LdifPrivate
28{
29 public:
30 int mModType;
31 bool mDelOldRdn, mUrl;
32 LdapDN mDn;
33 QString mAttr, mNewRdn, mNewSuperior, mOid;
34 QByteArray mLdif, mValue;
35 EntryType mEntryType;
36
37 bool mIsNewLine, mIsComment, mCritical;
38 ParseValue mLastParseValue;
39 uint mPos, mLineNumber;
40 QByteArray mLine;
41};
42
43Ldif::Ldif() : d( new LdifPrivate )
44{
45 startParsing();
46}
47
48Ldif::Ldif( const Ldif &that ) : d( new LdifPrivate )
49{
50 *d = *that.d;
51
52 startParsing();
53}
54
55Ldif &Ldif::operator=( const Ldif &that )
56{
57 if ( this == &that ) {
58 return *this;
59 }
60
61 *d = *that.d;
62
63 return *this;
64}
65
66Ldif::~Ldif()
67{
68 delete d;
69}
70
71QByteArray Ldif::assembleLine( const QString &fieldname,
72 const QByteArray &value,
73 uint linelen, bool url )
74{
75 bool safe = false;
76 bool isDn;
77 QByteArray result;
78
79 if ( url ) {
80 result = fieldname.toUtf8() + ":< " + value;
81 } else {
82 isDn = fieldname.toLower() == QLatin1String("dn");
83 //SAFE-INIT-CHAR
84 if ( value.size() > 0 && value[0] > 0 && value[0] != '\n' &&
85 value[0] != '\r' && value[0] != ':' && value[0] != '<' ) safe = true;
86
87 //SAFE-CHAR
88 if ( safe ) {
89 for ( int i=1; i < value.size(); i++ ) {
90 //allow utf-8 in Distinguished Names
91 if ( ( isDn && value[i] == 0 ) ||
92 ( !isDn && value[i] <= 0 ) ||
93 value[i] == '\r' || value[i] == '\n' ) {
94 safe = false;
95 break;
96 }
97 }
98 }
99
100 if ( value.size() == 0 ) {
101 safe = true;
102 }
103
104 if ( safe ) {
105 result = fieldname.toUtf8() + ": " + value;
106 } else {
107 result = fieldname.toUtf8() + ":: " + value.toBase64();
108 }
109
110 if ( linelen > 0 ) {
111 int i = (uint)( fieldname.length() + 2 ) > linelen ? fieldname.length() + 2 : linelen;
112 while ( i < result.length() ) {
113 result.insert( i, "\n " );
114 i += linelen+2;
115 }
116 }
117 }
118 return result;
119}
120
121QByteArray Ldif::assembleLine( const QString &fieldname, const QString &value,
122 uint linelen, bool url )
123{
124 return assembleLine( fieldname, value.toUtf8(), linelen, url );
125}
126
127bool Ldif::splitLine( const QByteArray &line, QString &fieldname, QByteArray &value )
128{
129 int position;
130 QByteArray tmp;
131 int linelen;
132
133// kDebug() << "line:" << QString::fromUtf8(line);
134
135 position = line.indexOf( ":" );
136 if ( position == -1 ) {
137 // strange: we did not find a fieldname
138 fieldname = QLatin1String("");
139 value = line.trimmed();
140// kDebug() << "value :" << value[0];
141 return false;
142 }
143
144 linelen = line.size();
145 fieldname = QString::fromUtf8( line.left( position ).trimmed() );
146
147 if ( linelen > ( position + 1 ) && line[ position + 1 ] == ':' ) {
148 // String is BASE64 encoded -> decode it now.
149 if ( linelen <= ( position + 3 ) ) {
150 value.resize( 0 );
151 return false;
152 }
153 value = QByteArray::fromBase64( line.mid( position + 3 ) );
154 return false;
155 }
156
157 if ( linelen > ( position + 1 ) && line[ position + 1 ] == '<' ) {
158 // String is an URL.
159 if ( linelen <= ( position + 3 ) ) {
160 value.resize( 0 );
161 return false;
162 }
163 value = QByteArray::fromBase64( line.mid( position + 3 ) );
164 return true;
165 }
166
167 if ( linelen <= ( position + 2 ) ) {
168 value.resize( 0 );
169 return false;
170 }
171 value = line.mid( position + 2 );
172 return false;
173}
174
175bool Ldif::splitControl( const QByteArray &line, QString &oid, bool &critical,
176 QByteArray &value )
177{
178 QString tmp;
179 critical = false;
180 bool url = splitLine( line, tmp, value );
181
182 kDebug() << "value:" << QString::fromUtf8( value, value.size() );
183 if ( tmp.isEmpty() ) {
184 tmp = QString::fromUtf8( value, value.size() );
185 value.resize( 0 );
186 }
187 if ( tmp.endsWith( QLatin1String( "true" ) ) ) {
188 critical = true;
189 tmp.truncate( tmp.length() - 5 );
190 } else if ( tmp.endsWith( QLatin1String( "false" ) ) ) {
191 critical = false;
192 tmp.truncate( tmp.length() - 6 );
193 }
194 oid = tmp;
195 return url;
196}
197
198Ldif::ParseValue Ldif::processLine()
199{
200
201 if ( d->mIsComment ) {
202 return None;
203 }
204
205 ParseValue retval = None;
206 if ( d->mLastParseValue == EndEntry ) {
207 d->mEntryType = Entry_None;
208 }
209
210 d->mUrl = splitLine( d->mLine, d->mAttr, d->mValue );
211
212 QString attrLower = d->mAttr.toLower();
213
214 switch ( d->mEntryType ) {
215 case Entry_None:
216 if ( attrLower == QLatin1String( "version" ) ) {
217 if ( !d->mDn.isEmpty() ) {
218 retval = Err;
219 }
220 } else if ( attrLower == QLatin1String( "dn" ) ) {
221 kDebug() << "ldapentry dn:" << QString::fromUtf8( d->mValue, d->mValue.size() );
222 d->mDn = LdapDN( QString::fromUtf8( d->mValue, d->mValue.size() ) );
223 d->mModType = Mod_None;
224 retval = NewEntry;
225 } else if ( attrLower == QLatin1String( "changetype" ) ) {
226 if ( d->mDn.isEmpty() ) {
227 retval = Err;
228 } else {
229 QString tmpval = QString::fromUtf8( d->mValue, d->mValue.size() );
230 kDebug() << "changetype:" << tmpval;
231 if ( tmpval == QLatin1String( "add" ) ) {
232 d->mEntryType = Entry_Add;
233 } else if ( tmpval == QLatin1String( "delete" ) ) {
234 d->mEntryType = Entry_Del;
235 } else if ( tmpval == QLatin1String( "modrdn" ) || tmpval == QLatin1String( "moddn" ) ) {
236 d->mNewRdn.clear();
237 d->mNewSuperior.clear();
238 d->mDelOldRdn = true;
239 d->mEntryType = Entry_Modrdn;
240 } else if ( tmpval == QLatin1String( "modify" ) ) {
241 d->mEntryType = Entry_Mod;
242 } else {
243 retval = Err;
244 }
245 }
246 } else if ( attrLower == QLatin1String( "control" ) ) {
247 d->mUrl = splitControl( d->mValue, d->mOid, d->mCritical, d->mValue );
248 retval = Control;
249 } else if ( !d->mAttr.isEmpty() && d->mValue.size() > 0 ) {
250 d->mEntryType = Entry_Add;
251 retval = Item;
252 }
253 break;
254 case Entry_Add:
255 if ( d->mAttr.isEmpty() && d->mValue.size() == 0 ) {
256 retval = EndEntry;
257 } else {
258 retval = Item;
259 }
260 break;
261 case Entry_Del:
262 if ( d->mAttr.isEmpty() && d->mValue.size() == 0 ) {
263 retval = EndEntry;
264 } else {
265 retval = Err;
266 }
267 break;
268 case Entry_Mod:
269 if ( d->mModType == Mod_None ) {
270 kDebug() << "new modtype" << d->mAttr;
271 if ( d->mAttr.isEmpty() && d->mValue.size() == 0 ) {
272 retval = EndEntry;
273 } else if ( attrLower == QLatin1String( "add" ) ) {
274 d->mModType = Mod_Add;
275 } else if ( attrLower == QLatin1String( "replace" ) ) {
276 d->mModType = Mod_Replace;
277 d->mAttr = QString::fromUtf8( d->mValue, d->mValue.size() );
278 d->mValue = QByteArray();
279 retval = Item;
280 } else if ( attrLower == QLatin1String( "delete" ) ) {
281 d->mModType = Mod_Del;
282 d->mAttr = QString::fromUtf8( d->mValue, d->mValue.size() );
283 d->mValue = QByteArray();
284 retval = Item;
285 } else {
286 retval = Err;
287 }
288 } else {
289 if ( d->mAttr.isEmpty() ) {
290 if ( QString::fromUtf8( d->mValue, d->mValue.size() ) == QLatin1String("-") ) {
291 d->mModType = Mod_None;
292 } else if ( d->mValue.size() == 0 ) {
293 retval = EndEntry;
294 } else {
295 retval = Err;
296 }
297 } else {
298 retval = Item;
299 }
300 }
301 break;
302 case Entry_Modrdn:
303 if ( d->mAttr.isEmpty() && d->mValue.size() == 0 ) {
304 retval = EndEntry;
305 } else if ( attrLower == QLatin1String( "newrdn" ) ) {
306 d->mNewRdn = QString::fromUtf8( d->mValue, d->mValue.size() );
307 } else if ( attrLower == QLatin1String( "newsuperior" ) ) {
308 d->mNewSuperior = QString::fromUtf8( d->mValue, d->mValue.size() );
309 } else if ( attrLower == QLatin1String( "deleteoldrdn" ) ) {
310 if ( d->mValue.size() > 0 && d->mValue[0] == '0' ) {
311 d->mDelOldRdn = false;
312 } else if ( d->mValue.size() > 0 && d->mValue[0] == '1' ) {
313 d->mDelOldRdn = true;
314 } else {
315 retval = Err;
316 }
317 } else {
318 retval = Err;
319 }
320 break;
321 }
322 return retval;
323}
324
325Ldif::ParseValue Ldif::nextItem()
326{
327 ParseValue retval = None;
328 char c=0;
329
330 while ( retval == None ) {
331 if ( d->mPos < (uint)d->mLdif.size() ) {
332 c = d->mLdif[d->mPos];
333 d->mPos++;
334 if ( d->mIsNewLine && c == '\r' ) {
335 continue; //handle \n\r line end
336 }
337 if ( d->mIsNewLine && ( c == ' ' || c == '\t' ) ) { //line folding
338 d->mIsNewLine = false;
339 continue;
340 }
341 if ( d->mIsNewLine ) {
342 d->mIsNewLine = false;
343 retval = processLine();
344 d->mLastParseValue = retval;
345 d->mLine.resize( 0 );
346 d->mIsComment = ( c == '#' );
347 }
348 if ( c == '\n' || c == '\r' ) {
349 d->mLineNumber++;
350 d->mIsNewLine = true;
351 continue;
352 }
353 } else {
354 retval = MoreData;
355 break;
356 }
357
358 if ( !d->mIsComment ) {
359 d->mLine += c;
360 }
361 }
362 return retval;
363}
364
365void Ldif::endLdif()
366{
367 QByteArray tmp( 3, '\n' );
368 d->mLdif = tmp;
369 d->mPos = 0;
370}
371
372void Ldif::startParsing()
373{
374 d->mPos = d->mLineNumber = 0;
375 d->mDelOldRdn = false;
376 d->mEntryType = Entry_None;
377 d->mModType = Mod_None;
378 d->mDn = LdapDN();
379 d->mNewRdn.clear();
380 d->mNewSuperior.clear();
381 d->mLine = QByteArray();
382 d->mIsNewLine = false;
383 d->mIsComment = false;
384 d->mLastParseValue = None;
385}
386
387void Ldif::setLdif( const QByteArray &ldif )
388{
389 d->mLdif = ldif;
390 d->mPos = 0;
391}
392
393Ldif::EntryType Ldif::entryType() const
394{
395 return d->mEntryType;
396}
397
398int Ldif::modType() const
399{
400 return d->mModType;
401}
402
403LdapDN Ldif::dn() const
404{
405 return d->mDn;
406}
407
408QString Ldif::newRdn() const
409{
410 return d->mNewRdn;
411}
412
413QString Ldif::newSuperior() const
414{
415 return d->mNewSuperior;
416}
417
418bool Ldif::delOldRdn() const
419{
420 return d->mDelOldRdn;
421}
422
423QString Ldif::attr() const
424{
425 return d->mAttr;
426}
427
428QByteArray Ldif::value() const
429{
430 return d->mValue;
431}
432
433bool Ldif::isUrl() const
434{
435 return d->mUrl;
436}
437
438bool Ldif::isCritical() const
439{
440 return d->mCritical;
441}
442
443QString Ldif::oid() const
444{
445 return d->mOid;
446}
447
448uint Ldif::lineNumber() const
449{
450 return d->mLineNumber;
451}
452