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