File indexing completed on 2025-01-26 04:52:15

0001 /* kldapclient.cpp - LDAP access
0002  * SPDX-FileCopyrightText: 2002 Klarälvdalens Datakonsult AB
0003  * SPDX-FileContributor: Steffen Hansen <hansen@kde.org>
0004  *
0005  * Ported to KABC by Daniel Molkentin <molkentin@kde.org>
0006  *
0007  * SPDX-License-Identifier: LGPL-2.0-or-later
0008  */
0009 
0010 #include "ldapclient.h"
0011 #include "ldapclient_debug.h"
0012 
0013 #include "kldapcore/ldapobject.h"
0014 #include "kldapcore/ldapserver.h"
0015 #include "kldapcore/ldapurl.h"
0016 #include "kldapcore/ldif.h"
0017 
0018 #include <KIO/TransferJob>
0019 
0020 #include <QPointer>
0021 
0022 using namespace KLDAPCore;
0023 using namespace KLDAPWidgets;
0024 class Q_DECL_HIDDEN LdapClient::LdapClientPrivate
0025 {
0026 public:
0027     LdapClientPrivate(LdapClient *qq)
0028         : q(qq)
0029     {
0030     }
0031 
0032     ~LdapClientPrivate()
0033     {
0034         cancelQuery();
0035     }
0036 
0037     void cancelQuery();
0038 
0039     void startParseLDIF();
0040     void parseLDIF(const QByteArray &data);
0041     void endParseLDIF();
0042     void finishCurrentObject();
0043 
0044     void slotData(KIO::Job *, const QByteArray &data);
0045     void slotInfoMessage(KJob *, const QString &info);
0046     void slotDone();
0047 
0048     LdapClient *const q;
0049 
0050     KLDAPCore::LdapServer mServer;
0051     QString mScope;
0052     QStringList mAttrs;
0053 
0054     QPointer<KJob> mJob = nullptr;
0055     bool mActive = false;
0056 
0057     KLDAPCore::LdapObject mCurrentObject;
0058     KLDAPCore::Ldif mLdif;
0059     int mClientNumber = 0;
0060     int mCompletionWeight = 0;
0061 };
0062 
0063 LdapClient::LdapClient(int clientNumber, QObject *parent)
0064     : QObject(parent)
0065     , d(new LdapClientPrivate(this))
0066 {
0067     d->mClientNumber = clientNumber;
0068     d->mCompletionWeight = 50 - d->mClientNumber;
0069 }
0070 
0071 LdapClient::~LdapClient() = default;
0072 
0073 bool LdapClient::isActive() const
0074 {
0075     return d->mActive;
0076 }
0077 
0078 void LdapClient::setServer(const KLDAPCore::LdapServer &server)
0079 {
0080     d->mServer = server;
0081 }
0082 
0083 const KLDAPCore::LdapServer LdapClient::server() const
0084 {
0085     return d->mServer;
0086 }
0087 
0088 void LdapClient::setAttributes(const QStringList &attrs)
0089 {
0090     d->mAttrs = attrs;
0091     d->mAttrs << QStringLiteral("objectClass"); // via objectClass we detect distribution lists
0092 }
0093 
0094 QStringList LdapClient::attributes() const
0095 {
0096     return d->mAttrs;
0097 }
0098 
0099 void LdapClient::setScope(const QString &scope)
0100 {
0101     d->mScope = scope;
0102 }
0103 
0104 void LdapClient::startQuery(const QString &filter)
0105 {
0106     cancelQuery();
0107     KLDAPCore::LdapUrl url{d->mServer.url()};
0108 
0109     url.setAttributes(d->mAttrs);
0110     url.setScope(d->mScope == QLatin1StringView("one") ? KLDAPCore::LdapUrl::One : KLDAPCore::LdapUrl::Sub);
0111     const QString userFilter = url.filter();
0112     QString finalFilter = filter;
0113     // combine the filter set by the user in the config dialog (url.filter()) and the filter from this query
0114     if (!userFilter.isEmpty()) {
0115         finalFilter = QLatin1StringView("&(") + finalFilter + QLatin1StringView(")(") + userFilter + QLatin1Char(')');
0116     }
0117     url.setFilter(QLatin1Char('(') + finalFilter + QLatin1Char(')'));
0118 
0119     qCDebug(LDAPCLIENT_LOG) << "LdapClient: Doing query:" << url.toDisplayString();
0120 
0121     d->startParseLDIF();
0122     d->mActive = true;
0123     KIO::TransferJob *transfertJob = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
0124     d->mJob = transfertJob;
0125     connect(transfertJob, &KIO::TransferJob::data, this, [this](KIO::Job *job, const QByteArray &data) {
0126         d->slotData(job, data);
0127     });
0128     connect(d->mJob.data(), &KIO::TransferJob::infoMessage, this, [this](KJob *job, const QString &message) {
0129         d->slotInfoMessage(job, message);
0130     });
0131     connect(d->mJob.data(), &KIO::TransferJob::result, this, [this]() {
0132         d->slotDone();
0133     });
0134 }
0135 
0136 void LdapClient::cancelQuery()
0137 {
0138     d->cancelQuery();
0139 }
0140 
0141 void LdapClient::LdapClientPrivate::cancelQuery()
0142 {
0143     if (mJob) {
0144         mJob->kill();
0145         mJob = nullptr;
0146     }
0147 
0148     mActive = false;
0149 }
0150 
0151 void LdapClient::LdapClientPrivate::slotData(KIO::Job *, const QByteArray &data)
0152 {
0153     parseLDIF(data);
0154 }
0155 
0156 void LdapClient::LdapClientPrivate::slotInfoMessage(KJob *, const QString &info)
0157 {
0158     qCDebug(LDAPCLIENT_LOG) << "Job said :" << info;
0159 }
0160 
0161 void LdapClient::LdapClientPrivate::slotDone()
0162 {
0163     endParseLDIF();
0164     mActive = false;
0165     if (!mJob) {
0166         return;
0167     }
0168     int err = mJob->error();
0169     if (err && err != KIO::ERR_USER_CANCELED) {
0170         Q_EMIT q->error(mJob->errorString());
0171     }
0172     Q_EMIT q->done();
0173 }
0174 
0175 void LdapClient::LdapClientPrivate::startParseLDIF()
0176 {
0177     mCurrentObject.clear();
0178     mLdif.startParsing();
0179 }
0180 
0181 void LdapClient::LdapClientPrivate::endParseLDIF()
0182 {
0183 }
0184 
0185 void LdapClient::LdapClientPrivate::finishCurrentObject()
0186 {
0187     mCurrentObject.setDn(mLdif.dn());
0188     KLDAPCore::LdapAttrValue objectclasses;
0189     const KLDAPCore::LdapAttrMap::ConstIterator end = mCurrentObject.attributes().constEnd();
0190     for (KLDAPCore::LdapAttrMap::ConstIterator it = mCurrentObject.attributes().constBegin(); it != end; ++it) {
0191         if (it.key().toLower() == QLatin1StringView("objectclass")) {
0192             objectclasses = it.value();
0193             break;
0194         }
0195     }
0196 
0197     bool groupofnames = false;
0198     const KLDAPCore::LdapAttrValue::ConstIterator endValue(objectclasses.constEnd());
0199     for (KLDAPCore::LdapAttrValue::ConstIterator it = objectclasses.constBegin(); it != endValue; ++it) {
0200         const QByteArray sClass = (*it).toLower();
0201         if (sClass == "groupofnames" || sClass == "kolabgroupofnames") {
0202             groupofnames = true;
0203         }
0204     }
0205 
0206     if (groupofnames) {
0207         KLDAPCore::LdapAttrMap::ConstIterator it = mCurrentObject.attributes().find(QStringLiteral("mail"));
0208         if (it == mCurrentObject.attributes().end()) {
0209             // No explicit mail address found so far?
0210             // Fine, then we use the address stored in the DN.
0211             QString sMail;
0212             const QStringList lMail = mCurrentObject.dn().toString().split(QStringLiteral(",dc="), Qt::SkipEmptyParts);
0213             const int n = lMail.count();
0214             if (n) {
0215                 if (lMail.first().startsWith(QLatin1StringView("cn="), Qt::CaseInsensitive)) {
0216                     sMail = lMail.first().simplified().mid(3);
0217                     if (1 < n) {
0218                         sMail.append(QLatin1Char('@'));
0219                     }
0220                     for (int i = 1; i < n; ++i) {
0221                         sMail.append(lMail.at(i));
0222                         if (i < n - 1) {
0223                             sMail.append(QLatin1Char('.'));
0224                         }
0225                     }
0226                     mCurrentObject.addValue(QStringLiteral("mail"), sMail.toUtf8());
0227                 }
0228             }
0229         }
0230     }
0231     Q_EMIT q->result(*q, mCurrentObject);
0232     mCurrentObject.clear();
0233 }
0234 
0235 void LdapClient::LdapClientPrivate::parseLDIF(const QByteArray &data)
0236 {
0237     // qCDebug(LDAPCLIENT_LOG) <<"LdapClient::parseLDIF(" << QCString(data.data(), data.size()+1) <<" )";
0238     if (!data.isEmpty()) {
0239         mLdif.setLdif(data);
0240     } else {
0241         mLdif.endLdif();
0242     }
0243     KLDAPCore::Ldif::ParseValue ret;
0244     QString name;
0245     do {
0246         ret = mLdif.nextItem();
0247         switch (ret) {
0248         case KLDAPCore::Ldif::Item: {
0249             name = mLdif.attr();
0250             const QByteArray value = mLdif.value();
0251             mCurrentObject.addValue(name, value);
0252             break;
0253         }
0254         case KLDAPCore::Ldif::EndEntry:
0255             finishCurrentObject();
0256             break;
0257         default:
0258             break;
0259         }
0260     } while (ret != KLDAPCore::Ldif::MoreData);
0261 }
0262 
0263 int LdapClient::clientNumber() const
0264 {
0265     return d->mClientNumber;
0266 }
0267 
0268 int LdapClient::completionWeight() const
0269 {
0270     return d->mCompletionWeight;
0271 }
0272 
0273 void LdapClient::setCompletionWeight(int weight)
0274 {
0275     d->mCompletionWeight = weight;
0276 }
0277 
0278 #include "moc_ldapclient.cpp"