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

source code of kcontacts/src/ldif.cpp