KLdap

ldif.cpp
1/*
2 This file is part of libkldap.
3 SPDX-FileCopyrightText: 2004-2006 Szombathelyi György <gyurco@freemail.hu>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "ldif.h"
9
10#include "ldap_core_debug.h"
11
12using namespace KLDAPCore;
13using namespace Qt::Literals::StringLiterals;
14class Q_DECL_HIDDEN Ldif::LdifPrivate
15{
16public:
17 int mModType;
18 bool mDelOldRdn, mUrl;
19 LdapDN 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() = default;
56
57QByteArray Ldif::assembleLine(const QString &fieldname, const QByteArray &value, uint linelen, bool url)
58{
59 QByteArray result;
60
61 if (url) {
62 result = fieldname.toUtf8() + ":< " + value;
63 } else {
64 bool safe = false;
65 bool isDn = fieldname.toLower() == "dn"_L1;
66 // SAFE-INIT-CHAR
67 if (!value.isEmpty() && value[0] > 0 && value[0] != '\n' && value[0] != '\r' && value[0] != ':' && value[0] != '<') {
68 safe = true;
69 }
70
71 // SAFE-CHAR
72 if (safe) {
73 for (int i = 1; i < value.size(); i++) {
74 // allow utf-8 in Distinguished Names
75 if ((isDn && value[i] == 0) || (!isDn && value[i] <= 0) || value[i] == '\r' || value[i] == '\n') {
76 safe = false;
77 break;
78 }
79 }
80 }
81
82 if (value.isEmpty()) {
83 safe = true;
84 }
85
86 if (safe) {
87 result = fieldname.toUtf8() + ": " + value;
88 } else {
89 result = fieldname.toUtf8() + ":: " + value.toBase64();
90 }
91
92 if (linelen > 0) {
93 int i = (uint)(fieldname.length() + 2) > linelen ? fieldname.length() + 2 : linelen;
94 while (i < result.length()) {
95 result.insert(i, "\n ");
96 i += linelen + 2;
97 }
98 }
99 }
100 return result;
101}
102
103QByteArray Ldif::assembleLine(const QString &fieldname, const QString &value, uint linelen, bool url)
104{
105 return assembleLine(fieldname, value.toUtf8(), linelen, url);
106}
107
108bool Ldif::splitLine(const QByteArray &line, QString &fieldname, QByteArray &value)
109{
110 int position;
111 int linelen;
112
113 // qCDebug(LDAP_CORE_LOG) << "line:" << QString::fromUtf8(line);
114
115 position = line.indexOf(":");
116 if (position == -1) {
117 // strange: we did not find a fieldname
118 fieldname = ""_L1;
119 value = line.trimmed();
120 // qCDebug(LDAP_CORE_LOG) << "value :" << value[0];
121 return false;
122 }
123
124 linelen = line.size();
125 fieldname = QString::fromUtf8(line.left(position).trimmed());
126
127 if (linelen > (position + 1) && line[position + 1] == ':') {
128 // String is BASE64 encoded -> decode it now.
129 if (linelen <= (position + 3)) {
130 value.resize(0);
131 return false;
132 }
133 value = QByteArray::fromBase64(line.mid(position + 3));
134 return false;
135 }
136
137 if (linelen > (position + 1) && line[position + 1] == '<') {
138 // String is an URL.
139 if (linelen <= (position + 3)) {
140 value.resize(0);
141 return false;
142 }
143 value = QByteArray::fromBase64(line.mid(position + 3));
144 return true;
145 }
146
147 if (linelen <= (position + 2)) {
148 value.resize(0);
149 return false;
150 }
151 value = line.mid(position + 2);
152 return false;
153}
154
155bool Ldif::splitControl(const QByteArray &line, QString &oid, bool &critical, QByteArray &value)
156{
157 QString tmp;
158 critical = false;
159 bool url = splitLine(line, tmp, value);
160
161 qCDebug(LDAP_CORE_LOG) << "value:" << QString::fromUtf8(value);
162 if (tmp.isEmpty()) {
164 value.resize(0);
165 }
166 if (tmp.endsWith("true"_L1)) {
167 critical = true;
168 tmp.chop(5);
169 } else if (tmp.endsWith("false"_L1)) {
170 critical = false;
171 tmp.chop(6);
172 }
173 oid = tmp;
174 return url;
175}
176
177Ldif::ParseValue Ldif::processLine()
178{
179 if (d->mIsComment) {
180 return None;
181 }
182
183 ParseValue retval = None;
184 if (d->mLastParseValue == EndEntry) {
185 d->mEntryType = Entry_None;
186 }
187
188 d->mUrl = splitLine(d->mLine, d->mAttr, d->mValue);
189
190 const QString attrLower = d->mAttr.toLower();
191
192 switch (d->mEntryType) {
193 case Entry_None:
194 if (attrLower == "version"_L1) {
195 if (!d->mDn.isEmpty()) {
196 retval = Err;
197 }
198 } else if (attrLower == "dn"_L1) {
199 qCDebug(LDAP_CORE_LOG) << "ldapentry dn:" << QString::fromUtf8(d->mValue);
200 d->mDn = LdapDN(QString::fromUtf8(d->mValue));
201 d->mModType = Mod_None;
202 retval = NewEntry;
203 } else if (attrLower == "changetype"_L1) {
204 if (d->mDn.isEmpty()) {
205 retval = Err;
206 } else {
207 QString tmpval = QString::fromUtf8(d->mValue);
208 qCDebug(LDAP_CORE_LOG) << "changetype:" << tmpval;
209 if (tmpval == "add"_L1) {
210 d->mEntryType = Entry_Add;
211 } else if (tmpval == "delete"_L1) {
212 d->mEntryType = Entry_Del;
213 } else if (tmpval == "modrdn"_L1 || tmpval == "moddn"_L1) {
214 d->mNewRdn.clear();
215 d->mNewSuperior.clear();
216 d->mDelOldRdn = true;
217 d->mEntryType = Entry_Modrdn;
218 } else if (tmpval == "modify"_L1) {
219 d->mEntryType = Entry_Mod;
220 } else {
221 retval = Err;
222 }
223 }
224 } else if (attrLower == "control"_L1) {
225 d->mUrl = splitControl(d->mValue, d->mOid, d->mCritical, d->mValue);
226 retval = Control;
227 } else if (!d->mAttr.isEmpty() && !d->mValue.isEmpty()) {
228 d->mEntryType = Entry_Add;
229 retval = Item;
230 }
231 break;
232 case Entry_Add:
233 if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
234 retval = EndEntry;
235 } else {
236 retval = Item;
237 }
238 break;
239 case Entry_Del:
240 if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
241 retval = EndEntry;
242 } else {
243 retval = Err;
244 }
245 break;
246 case Entry_Mod:
247 if (d->mModType == Mod_None) {
248 qCDebug(LDAP_CORE_LOG) << "new modtype" << d->mAttr;
249 if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
250 retval = EndEntry;
251 } else if (attrLower == "add"_L1) {
252 d->mModType = Mod_Add;
253 } else if (attrLower == "replace"_L1) {
254 d->mModType = Mod_Replace;
255 d->mAttr = QString::fromUtf8(d->mValue);
256 d->mValue = QByteArray();
257 retval = Item;
258 } else if (attrLower == "delete"_L1) {
259 d->mModType = Mod_Del;
260 d->mAttr = QString::fromUtf8(d->mValue);
261 d->mValue = QByteArray();
262 retval = Item;
263 } else {
264 retval = Err;
265 }
266 } else {
267 if (d->mAttr.isEmpty()) {
268 if (QString::fromUtf8(d->mValue) == "-"_L1) {
269 d->mModType = Mod_None;
270 } else if (d->mValue.isEmpty()) {
271 retval = EndEntry;
272 } else {
273 retval = Err;
274 }
275 } else {
276 retval = Item;
277 }
278 }
279 break;
280 case Entry_Modrdn:
281 if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
282 retval = EndEntry;
283 } else if (attrLower == "newrdn"_L1) {
284 d->mNewRdn = QString::fromUtf8(d->mValue);
285 } else if (attrLower == "newsuperior"_L1) {
286 d->mNewSuperior = QString::fromUtf8(d->mValue);
287 } else if (attrLower == "deleteoldrdn"_L1) {
288 if (d->mValue.size() > 0 && d->mValue[0] == '0') {
289 d->mDelOldRdn = false;
290 } else if (d->mValue.size() > 0 && d->mValue[0] == '1') {
291 d->mDelOldRdn = true;
292 } else {
293 retval = Err;
294 }
295 } else {
296 retval = Err;
297 }
298 break;
299 }
300 return retval;
301}
302
303Ldif::ParseValue Ldif::nextItem()
304{
305 ParseValue retval = None;
306 char c = 0;
307
308 while (retval == None) {
309 if (d->mPos < (uint)d->mLdif.size()) {
310 c = d->mLdif.at(d->mPos);
311 d->mPos++;
312 if (d->mIsNewLine && c == '\r') {
313 continue; // handle \n\r line end
314 }
315 if (d->mIsNewLine && (c == ' ' || c == '\t')) { // line folding
316 d->mIsNewLine = false;
317 continue;
318 }
319 if (d->mIsNewLine) {
320 d->mIsNewLine = false;
321 retval = processLine();
322 d->mLastParseValue = retval;
323 d->mLine.resize(0);
324 d->mIsComment = (c == '#');
325 }
326 if (c == '\n' || c == '\r') {
327 d->mLineNumber++;
328 d->mIsNewLine = true;
329 continue;
330 }
331 } else {
332 retval = MoreData;
333 break;
334 }
335
336 if (!d->mIsComment) {
337 d->mLine += c;
338 }
339 }
340 return retval;
341}
342
344{
345 QByteArray tmp(3, '\n');
346 d->mLdif = tmp;
347 d->mPos = 0;
348}
349
351{
352 d->mPos = d->mLineNumber = 0;
353 d->mDelOldRdn = false;
354 d->mEntryType = Entry_None;
355 d->mModType = Mod_None;
356 d->mDn = LdapDN();
357 d->mNewRdn.clear();
358 d->mNewSuperior.clear();
359 d->mLine = QByteArray();
360 d->mIsNewLine = false;
361 d->mIsComment = false;
362 d->mLastParseValue = None;
363}
364
365void Ldif::setLdif(const QByteArray &ldif)
366{
367 d->mLdif = ldif;
368 d->mPos = 0;
369}
370
371Ldif::EntryType Ldif::entryType() const
372{
373 return d->mEntryType;
374}
375
376int Ldif::modType() const
377{
378 return d->mModType;
379}
380
381LdapDN Ldif::dn() const
382{
383 return d->mDn;
384}
385
387{
388 return d->mNewRdn;
389}
390
392{
393 return d->mNewSuperior;
394}
395
396bool Ldif::delOldRdn() const
397{
398 return d->mDelOldRdn;
399}
400
402{
403 return d->mAttr;
404}
405
407{
408 return d->mValue;
409}
410
411bool Ldif::isUrl() const
412{
413 return d->mUrl;
414}
415
417{
418 return d->mCritical;
419}
420
422{
423 return d->mOid;
424}
425
427{
428 return d->mLineNumber;
429}
Ldif.
Definition ldif.h:29
int modType() const
Returns the LDAP modify request type if entryType() returned Entry_Mod.
Definition ldif.cpp:376
ParseValue processLine()
Process one Ldif line.
Definition ldif.cpp:177
static QByteArray assembleLine(const QString &fieldname, const QByteArray &value, uint linelen=0, bool url=false)
Assembles fieldname and value into a valid Ldif line, BASE64 encodes the value if necessary and optio...
Definition ldif.cpp:57
void endLdif()
Indicates the end of the Ldif file/stream.
Definition ldif.cpp:343
uint lineNumber() const
Returns the line number which the parser processes.
Definition ldif.cpp:426
QByteArray value() const
Returns the attribute value.
Definition ldif.cpp:406
ParseValue nextItem()
Process the Ldif until a complete item can be returned.
Definition ldif.cpp:303
bool delOldRdn() const
Returns if the delete of the old RDN is required.
Definition ldif.cpp:396
bool isUrl() const
Returns if val() is an url.
Definition ldif.cpp:411
QString oid() const
Returns the OID when modType() returned Control.
Definition ldif.cpp:421
QString attr() const
Returns the attribute name.
Definition ldif.cpp:401
static bool splitControl(const QByteArray &line, QString &oid, bool &critical, QByteArray &value)
Splits a control specification (without the "control:" directive)
Definition ldif.cpp:155
void startParsing()
Starts the parsing of a new Ldif.
Definition ldif.cpp:350
void setLdif(const QByteArray &ldif)
Sets a chunk of Ldif.
Definition ldif.cpp:365
LdapDN dn() const
Returns the Distinguished Name of the current entry.
Definition ldif.cpp:381
bool isCritical() const
Returns the criticality level when modType() returned Control.
Definition ldif.cpp:416
static bool splitLine(const QByteArray &line, QString &fieldname, QByteArray &value)
Splits one line from an Ldif file to attribute and value components.
Definition ldif.cpp:108
QString newRdn() const
Returns the new Relative Distinguished Name if modType() returned Entry_Modrdn.
Definition ldif.cpp:386
EntryType entryType() const
Returns the requested LDAP operation extracted from the current entry.
Definition ldif.cpp:371
QString newSuperior() const
Returns the new parent of the entry if modType() returned Entry_Modrdn.
Definition ldif.cpp:391
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
QByteArray & insert(qsizetype i, QByteArrayView data)
bool isEmpty() const const
QByteArray left(qsizetype len) const const
qsizetype length() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
void resize(qsizetype newSize, char c)
qsizetype size() const const
QByteArray toBase64(Base64Options options) const const
QByteArray trimmed() const const
void chop(qsizetype n)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString toLower() const const
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:34:09 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.