File indexing completed on 2024-04-14 03:51:26

0001 /*
0002     A temporary copy to break dependency to KLDAP
0003 
0004     This file is part of libkldap.
0005     SPDX-FileCopyrightText: 2004-2006 Szombathelyi György <gyurco@freemail.hu>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "ldif_p.h"
0011 
0012 #include "kcontacts_debug.h"
0013 
0014 class Q_DECL_HIDDEN Ldif::LdifPrivate
0015 {
0016 public:
0017     int mModType;
0018     bool mDelOldRdn, mUrl;
0019     QByteArray mDn;
0020     QString mAttr, mNewRdn, mNewSuperior, mOid;
0021     QByteArray mLdif, mValue;
0022     EntryType mEntryType;
0023 
0024     bool mIsNewLine, mIsComment, mCritical;
0025     ParseValue mLastParseValue;
0026     uint mPos, mLineNumber;
0027     QByteArray mLine;
0028 };
0029 
0030 Ldif::Ldif()
0031     : d(new LdifPrivate)
0032 {
0033     startParsing();
0034 }
0035 
0036 Ldif::Ldif(const Ldif &that)
0037     : d(new LdifPrivate)
0038 {
0039     *d = *that.d;
0040 
0041     startParsing();
0042 }
0043 
0044 Ldif &Ldif::operator=(const Ldif &that)
0045 {
0046     if (this == &that) {
0047         return *this;
0048     }
0049 
0050     *d = *that.d;
0051 
0052     return *this;
0053 }
0054 
0055 Ldif::~Ldif()
0056 {
0057     delete d;
0058 }
0059 
0060 QByteArray Ldif::assembleLine(const QString &fieldname, const QByteArray &value, uint linelen, bool url)
0061 {
0062     QByteArray result;
0063 
0064     if (url) {
0065         result = fieldname.toUtf8() + ":< " + value;
0066     } else {
0067         bool safe = false;
0068         bool isDn = fieldname.toLower() == QLatin1String("dn");
0069         // SAFE-INIT-CHAR
0070         if (value.size() > 0 && value[0] > 0 && value[0] != '\n' //
0071             && value[0] != '\r' && value[0] != ':' && value[0] != '<') {
0072             safe = true;
0073         }
0074 
0075         // SAFE-CHAR
0076         if (safe) {
0077             for (int i = 1; i < value.size(); ++i) {
0078                 // allow utf-8 in Distinguished Names
0079                 if ((isDn && value[i] == 0) //
0080                     || (!isDn && value[i] <= 0) //
0081                     || value[i] == '\r' || value[i] == '\n') {
0082                     safe = false;
0083                     break;
0084                 }
0085             }
0086         }
0087 
0088         if (value.isEmpty()) {
0089             safe = true;
0090         }
0091 
0092         if (safe) {
0093             result = fieldname.toUtf8() + ": " + value;
0094         } else {
0095             result = fieldname.toUtf8() + ":: " + value.toBase64();
0096         }
0097 
0098         if (linelen > 0) {
0099             int i = (uint)(fieldname.length() + 2) > linelen ? fieldname.length() + 2 : linelen;
0100             while (i < result.length()) {
0101                 result.insert(i, "\n ");
0102                 i += linelen + 2;
0103             }
0104         }
0105     }
0106     return result;
0107 }
0108 
0109 QByteArray Ldif::assembleLine(const QString &fieldname, const QString &value, uint linelen, bool url)
0110 {
0111     return assembleLine(fieldname, value.toUtf8(), linelen, url);
0112 }
0113 
0114 bool Ldif::splitLine(const QByteArray &line, QString &fieldname, QByteArray &value)
0115 {
0116     int position;
0117     int linelen;
0118 
0119     //  qCDebug(KCONTACTS_LOG) << "line:" << QString::fromUtf8(line);
0120 
0121     position = line.indexOf(":");
0122     if (position == -1) {
0123         // strange: we did not find a fieldname
0124         fieldname = QLatin1String("");
0125         value = line.trimmed();
0126         //    qCDebug(KCONTACTS_LOG) << "value :" << value[0];
0127         return false;
0128     }
0129 
0130     linelen = line.size();
0131     fieldname = QString::fromUtf8(line.left(position).trimmed());
0132 
0133     if (linelen > (position + 1) && line[position + 1] == ':') {
0134         // String is BASE64 encoded -> decode it now.
0135         if (linelen <= (position + 3)) {
0136             value.resize(0);
0137             return false;
0138         }
0139         value = QByteArray::fromBase64(line.mid(position + 3));
0140         return false;
0141     }
0142 
0143     if (linelen > (position + 1) && line[position + 1] == '<') {
0144         // String is an URL.
0145         if (linelen <= (position + 3)) {
0146             value.resize(0);
0147             return false;
0148         }
0149         value = QByteArray::fromBase64(line.mid(position + 3));
0150         return true;
0151     }
0152 
0153     if (linelen <= (position + 2)) {
0154         value.resize(0);
0155         return false;
0156     }
0157     value = line.mid(position + 2);
0158     return false;
0159 }
0160 
0161 bool Ldif::splitControl(const QByteArray &line, QString &oid, bool &critical, QByteArray &value)
0162 {
0163     QString tmp;
0164     critical = false;
0165     bool url = splitLine(line, tmp, value);
0166 
0167     qCDebug(KCONTACTS_LOG) << "value:" << QString::fromUtf8(value);
0168     if (tmp.isEmpty()) {
0169         tmp = QString::fromUtf8(value);
0170         value.resize(0);
0171     }
0172     if (tmp.endsWith(QLatin1String("true"))) {
0173         critical = true;
0174         tmp.chop(5);
0175     } else if (tmp.endsWith(QLatin1String("false"))) {
0176         critical = false;
0177         tmp.chop(6);
0178     }
0179     oid = tmp;
0180     return url;
0181 }
0182 
0183 Ldif::ParseValue Ldif::processLine()
0184 {
0185     if (d->mIsComment) {
0186         return None;
0187     }
0188 
0189     ParseValue retval = None;
0190     if (d->mLastParseValue == EndEntry) {
0191         d->mEntryType = Entry_None;
0192     }
0193 
0194     d->mUrl = splitLine(d->mLine, d->mAttr, d->mValue);
0195 
0196     QString attrLower = d->mAttr.toLower();
0197 
0198     switch (d->mEntryType) {
0199     case Entry_None:
0200         if (attrLower == QLatin1String("version")) {
0201             if (!d->mDn.isEmpty()) {
0202                 retval = Err;
0203             }
0204         } else if (attrLower == QLatin1String("dn")) {
0205             qCDebug(KCONTACTS_LOG) << "ldapentry dn:" << QString::fromUtf8(d->mValue);
0206             d->mDn = d->mValue;
0207             d->mModType = Mod_None;
0208             retval = NewEntry;
0209         } else if (attrLower == QLatin1String("changetype")) {
0210             if (d->mDn.isEmpty()) {
0211                 retval = Err;
0212             } else {
0213                 QString tmpval = QString::fromUtf8(d->mValue);
0214                 qCDebug(KCONTACTS_LOG) << "changetype:" << tmpval;
0215                 if (tmpval == QLatin1String("add")) {
0216                     d->mEntryType = Entry_Add;
0217                 } else if (tmpval == QLatin1String("delete")) {
0218                     d->mEntryType = Entry_Del;
0219                 } else if (tmpval == QLatin1String("modrdn") || tmpval == QLatin1String("moddn")) {
0220                     d->mNewRdn = QLatin1String("");
0221                     d->mNewSuperior = QLatin1String("");
0222                     d->mDelOldRdn = true;
0223                     d->mEntryType = Entry_Modrdn;
0224                 } else if (tmpval == QLatin1String("modify")) {
0225                     d->mEntryType = Entry_Mod;
0226                 } else {
0227                     retval = Err;
0228                 }
0229             }
0230         } else if (attrLower == QLatin1String("control")) {
0231             d->mUrl = splitControl(d->mValue, d->mOid, d->mCritical, d->mValue);
0232             retval = Control;
0233         } else if (!d->mAttr.isEmpty() && !d->mValue.isEmpty()) {
0234             d->mEntryType = Entry_Add;
0235             retval = Item;
0236         }
0237         break;
0238     case Entry_Add:
0239         if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
0240             retval = EndEntry;
0241         } else {
0242             retval = Item;
0243         }
0244         break;
0245     case Entry_Del:
0246         if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
0247             retval = EndEntry;
0248         } else {
0249             retval = Err;
0250         }
0251         break;
0252     case Entry_Mod:
0253         if (d->mModType == Mod_None) {
0254             qCDebug(KCONTACTS_LOG) << "new modtype" << d->mAttr;
0255             if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
0256                 retval = EndEntry;
0257             } else if (attrLower == QLatin1String("add")) {
0258                 d->mModType = Mod_Add;
0259             } else if (attrLower == QLatin1String("replace")) {
0260                 d->mModType = Mod_Replace;
0261                 d->mAttr = QString::fromUtf8(d->mValue);
0262                 d->mValue = QByteArray();
0263                 retval = Item;
0264             } else if (attrLower == QLatin1String("delete")) {
0265                 d->mModType = Mod_Del;
0266                 d->mAttr = QString::fromUtf8(d->mValue);
0267                 d->mValue = QByteArray();
0268                 retval = Item;
0269             } else {
0270                 retval = Err;
0271             }
0272         } else {
0273             if (d->mAttr.isEmpty()) {
0274                 if (QString::fromUtf8(d->mValue) == QLatin1String("-")) {
0275                     d->mModType = Mod_None;
0276                 } else if (d->mValue.isEmpty()) {
0277                     retval = EndEntry;
0278                 } else {
0279                     retval = Err;
0280                 }
0281             } else {
0282                 retval = Item;
0283             }
0284         }
0285         break;
0286     case Entry_Modrdn:
0287         if (d->mAttr.isEmpty() && d->mValue.isEmpty()) {
0288             retval = EndEntry;
0289         } else if (attrLower == QLatin1String("newrdn")) {
0290             d->mNewRdn = QString::fromUtf8(d->mValue);
0291         } else if (attrLower == QLatin1String("newsuperior")) {
0292             d->mNewSuperior = QString::fromUtf8(d->mValue);
0293         } else if (attrLower == QLatin1String("deleteoldrdn")) {
0294             if (d->mValue.size() > 0 && d->mValue[0] == '0') {
0295                 d->mDelOldRdn = false;
0296             } else if (d->mValue.size() > 0 && d->mValue[0] == '1') {
0297                 d->mDelOldRdn = true;
0298             } else {
0299                 retval = Err;
0300             }
0301         } else {
0302             retval = Err;
0303         }
0304         break;
0305     }
0306     return retval;
0307 }
0308 
0309 Ldif::ParseValue Ldif::nextItem()
0310 {
0311     ParseValue retval = None;
0312     char c = 0;
0313 
0314     while (retval == None) {
0315         if (d->mPos < (uint)d->mLdif.size()) {
0316             c = d->mLdif[d->mPos];
0317             d->mPos++;
0318             if (d->mIsNewLine && c == '\r') {
0319                 continue; // handle \n\r line end
0320             }
0321             if (d->mIsNewLine && (c == ' ' || c == '\t')) { // line folding
0322                 d->mIsNewLine = false;
0323                 continue;
0324             }
0325             if (d->mIsNewLine) {
0326                 d->mIsNewLine = false;
0327                 retval = processLine();
0328                 d->mLastParseValue = retval;
0329                 d->mLine.resize(0);
0330                 d->mIsComment = (c == '#');
0331             }
0332             if (c == '\n' || c == '\r') {
0333                 d->mLineNumber++;
0334                 d->mIsNewLine = true;
0335                 continue;
0336             }
0337         } else {
0338             retval = MoreData;
0339             break;
0340         }
0341 
0342         if (!d->mIsComment) {
0343             d->mLine += c;
0344         }
0345     }
0346     return retval;
0347 }
0348 
0349 void Ldif::endLdif()
0350 {
0351     QByteArray tmp(3, '\n');
0352     d->mLdif = tmp;
0353     d->mPos = 0;
0354 }
0355 
0356 void Ldif::startParsing()
0357 {
0358     d->mPos = d->mLineNumber = 0;
0359     d->mDelOldRdn = false;
0360     d->mEntryType = Entry_None;
0361     d->mModType = Mod_None;
0362     d->mNewRdn.clear();
0363     d->mNewSuperior.clear();
0364     d->mLine = QByteArray();
0365     d->mIsNewLine = false;
0366     d->mIsComment = false;
0367     d->mLastParseValue = None;
0368 }
0369 
0370 void Ldif::setLdif(const QByteArray &ldif)
0371 {
0372     d->mLdif = ldif;
0373     d->mPos = 0;
0374 }
0375 
0376 Ldif::EntryType Ldif::entryType() const
0377 {
0378     return d->mEntryType;
0379 }
0380 
0381 int Ldif::modType() const
0382 {
0383     return d->mModType;
0384 }
0385 
0386 QString Ldif::newRdn() const
0387 {
0388     return d->mNewRdn;
0389 }
0390 
0391 QString Ldif::newSuperior() const
0392 {
0393     return d->mNewSuperior;
0394 }
0395 
0396 bool Ldif::delOldRdn() const
0397 {
0398     return d->mDelOldRdn;
0399 }
0400 
0401 QString Ldif::attr() const
0402 {
0403     return d->mAttr;
0404 }
0405 
0406 QByteArray Ldif::value() const
0407 {
0408     return d->mValue;
0409 }
0410 
0411 bool Ldif::isUrl() const
0412 {
0413     return d->mUrl;
0414 }
0415 
0416 bool Ldif::isCritical() const
0417 {
0418     return d->mCritical;
0419 }
0420 
0421 QString Ldif::oid() const
0422 {
0423     return d->mOid;
0424 }
0425 
0426 uint Ldif::lineNumber() const
0427 {
0428     return d->mLineNumber;
0429 }