File indexing completed on 2024-05-12 05:46:49

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