File indexing completed on 2024-12-22 04:59:50

0001 /*
0002   SPDX-FileCopyrightText: 2004-2007 Szombathelyi György <gyurco@freemail.hu>
0003 
0004   SPDX-License-Identifier: MIT
0005 
0006 */
0007 
0008 #include "kio_ldap.h"
0009 #include "kldap_debug.h"
0010 
0011 #include "kldapcore/ldif.h"
0012 
0013 #include <KLocalizedString>
0014 #include <QCoreApplication>
0015 #include <QDebug>
0016 
0017 #ifdef Q_OS_WIN
0018 #include <Winsock2.h>
0019 #else
0020 #include <netdb.h>
0021 #include <netinet/in.h>
0022 #endif
0023 #include <sys/stat.h>
0024 
0025 using namespace KIO;
0026 using namespace KLDAPCore;
0027 
0028 // Pseudo plugin class to embed meta data
0029 class KIOPluginForMetaData : public QObject
0030 {
0031     Q_OBJECT
0032     Q_PLUGIN_METADATA(IID "org.kde.kio.worker.ldap" FILE "ldap.json")
0033 };
0034 
0035 extern "C" {
0036 int Q_DECL_EXPORT kdemain(int argc, char **argv);
0037 }
0038 
0039 /**
0040  * The main program.
0041  */
0042 int kdemain(int argc, char **argv)
0043 {
0044     QCoreApplication app(argc, argv); // needed for QSocketNotifier
0045     app.setApplicationName(QStringLiteral("kio_ldap"));
0046 
0047     qCDebug(KLDAP_LOG) << "Starting kio_ldap instance";
0048 
0049     if (argc != 4) {
0050         qCritical() << "Usage kio_ldap protocol pool app";
0051         return -1;
0052     }
0053 
0054     // let the protocol class do its work
0055     LDAPProtocol worker(argv[1], argv[2], argv[3]);
0056     worker.dispatchLoop();
0057 
0058     qCDebug(KLDAP_LOG) << "Done";
0059     return 0;
0060 }
0061 
0062 /**
0063  * Initialize the ldap worker
0064  */
0065 LDAPProtocol::LDAPProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app)
0066     : WorkerBase(protocol, pool, app)
0067     , mProtocol(protocol)
0068 {
0069     mOp.setConnection(mConn);
0070     qCDebug(KLDAP_LOG) << "LDAPProtocol::LDAPProtocol (" << protocol << ")";
0071 }
0072 
0073 LDAPProtocol::~LDAPProtocol()
0074 {
0075     closeConnection();
0076 }
0077 
0078 KIO::WorkerResult LDAPProtocol::LDAPErr(int err)
0079 {
0080     QString extramsg;
0081     if (mConnected) {
0082         if (err == KLDAP_SUCCESS) {
0083             err = mConn.ldapErrorCode();
0084         }
0085         if (err != KLDAP_SUCCESS) {
0086             extramsg = i18n("\nAdditional info: ") + mConn.ldapErrorString();
0087         }
0088     }
0089     if (err == KLDAP_SUCCESS) {
0090         return KIO::WorkerResult::pass();
0091     }
0092 
0093     qCDebug(KLDAP_LOG) << "error code: " << err << " msg: " << LdapConnection::errorString(err) << extramsg << "'";
0094     QString msg;
0095     msg = mServer.url().toDisplayString();
0096     if (!extramsg.isEmpty()) {
0097         msg += extramsg;
0098     }
0099 
0100     /* FIXME: No need to close on all errors */
0101     closeConnection();
0102 
0103     switch (err) {
0104     /* FIXME: is it worth mapping the following error codes to kio errors?
0105 
0106       LDAP_OPERATIONS_ERROR
0107       LDAP_STRONG_AUTH_REQUIRED
0108       LDAP_PROTOCOL_ERROR
0109       LDAP_TIMELIMIT_EXCEEDED
0110       LDAP_SIZELIMIT_EXCEEDED
0111       LDAP_COMPARE_FALSE
0112       LDAP_COMPARE_TRUE
0113       LDAP_PARTIAL_RESULTS
0114       LDAP_NO_SUCH_ATTRIBUTE
0115       LDAP_UNDEFINED_TYPE
0116       LDAP_INAPPROPRIATE_MATCHING
0117       LDAP_CONSTRAINT_VIOLATION
0118       LDAP_INVALID_SYNTAX
0119       LDAP_NO_SUCH_OBJECT
0120       LDAP_ALIAS_PROBLEM
0121       LDAP_INVALID_DN_SYNTAX
0122       LDAP_IS_LEAF
0123       LDAP_ALIAS_DEREF_PROBLEM
0124       LDAP_INAPPROPRIATE_AUTH
0125       LDAP_BUSY
0126       LDAP_UNAVAILABLE
0127       LDAP_UNWILLING_TO_PERFORM
0128       LDAP_LOOP_DETECT
0129       LDAP_NAMING_VIOLATION
0130       LDAP_OBJECT_CLASS_VIOLATION
0131       LDAP_NOT_ALLOWED_ON_NONLEAF
0132       LDAP_NOT_ALLOWED_ON_RDN
0133       LDAP_NO_OBJECT_CLASS_MODS
0134       LDAP_OTHER
0135       LDAP_LOCAL_ERROR
0136       LDAP_ENCODING_ERROR
0137       LDAP_DECODING_ERROR
0138       LDAP_FILTER_ERROR
0139     */
0140     case KLDAP_AUTH_UNKNOWN:
0141     case KLDAP_INVALID_CREDENTIALS:
0142     case KLDAP_STRONG_AUTH_NOT_SUPPORTED:
0143         return KIO::WorkerResult::fail(ERR_CANNOT_AUTHENTICATE, msg);
0144     case KLDAP_ALREADY_EXISTS:
0145         return KIO::WorkerResult::fail(ERR_FILE_ALREADY_EXIST, msg);
0146     case KLDAP_INSUFFICIENT_ACCESS:
0147         return KIO::WorkerResult::fail(ERR_ACCESS_DENIED, msg);
0148     case KLDAP_CONNECT_ERROR:
0149     case KLDAP_SERVER_DOWN:
0150         return KIO::WorkerResult::fail(ERR_CANNOT_CONNECT, msg);
0151     case KLDAP_TIMEOUT:
0152         return KIO::WorkerResult::fail(ERR_SERVER_TIMEOUT, msg);
0153     case KLDAP_PARAM_ERROR:
0154         return KIO::WorkerResult::fail(ERR_INTERNAL, msg);
0155     case KLDAP_NO_MEMORY:
0156         return KIO::WorkerResult::fail(ERR_OUT_OF_MEMORY, msg);
0157 
0158     default:
0159         return KIO::WorkerResult::fail(
0160             KIO::ERR_WORKER_DEFINED,
0161             i18n("LDAP server returned the error: %1 %2\nThe LDAP URL was: %3", LdapConnection::errorString(err), extramsg, mServer.url().toDisplayString()));
0162     }
0163 }
0164 
0165 void LDAPProtocol::controlsFromMetaData(LdapControls &serverctrls, LdapControls &clientctrls)
0166 {
0167     QString oid;
0168     bool critical;
0169     QByteArray value;
0170     int i = 0;
0171     while (hasMetaData(QStringLiteral("SERVER_CTRL%1").arg(i))) {
0172         const QByteArray val = metaData(QStringLiteral("SERVER_CTRL%1").arg(i)).toUtf8();
0173         Ldif::splitControl(val, oid, critical, value);
0174         qCDebug(KLDAP_LOG) << "server ctrl #" << i << " value: " << val << " oid: " << oid << " critical: " << critical
0175                            << " value: " << QString::fromUtf8(value.constData(), value.size());
0176         LdapControl ctrl(oid, val, critical);
0177         serverctrls.append(ctrl);
0178         i++;
0179     }
0180     i = 0;
0181     while (hasMetaData(QStringLiteral("CLIENT_CTRL%1").arg(i))) {
0182         const QByteArray val = metaData(QStringLiteral("CLIENT_CTRL%1").arg(i)).toUtf8();
0183         Ldif::splitControl(val, oid, critical, value);
0184         qCDebug(KLDAP_LOG) << "client ctrl #" << i << " value: " << val << " oid: " << oid << " critical: " << critical
0185                            << " value: " << QString::fromUtf8(value.constData(), value.size());
0186         LdapControl ctrl(oid, val, critical);
0187         clientctrls.append(ctrl);
0188         i++;
0189     }
0190 }
0191 
0192 void LDAPProtocol::LDAPEntry2UDSEntry(const LdapDN &dn, UDSEntry &entry, const LdapUrl &usrc, bool dir)
0193 {
0194     int pos;
0195     entry.clear();
0196     QString name = dn.toString();
0197     if ((pos = name.indexOf(QLatin1Char(','))) > 0) {
0198         name.truncate(pos);
0199     }
0200     if ((pos = name.indexOf(QLatin1Char('='))) > 0) {
0201         name.remove(0, pos + 1);
0202     }
0203     name.replace(QLatin1Char(' '), QLatin1StringView("_"));
0204     if (!dir) {
0205         name += QLatin1StringView(".ldif");
0206     }
0207     entry.fastInsert(KIO::UDSEntry::UDS_NAME, name);
0208 
0209     // the file type
0210     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, dir ? S_IFDIR : S_IFREG);
0211 
0212     // the mimetype
0213     if (!dir) {
0214         entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("text/plain"));
0215     }
0216 
0217     entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, dir ? 0500 : 0400);
0218 
0219     // the url
0220     LdapUrl url = usrc;
0221     url.setPath(QLatin1Char('/') + dn.toString());
0222     url.setScope(dir ? LdapUrl::One : LdapUrl::Base);
0223     entry.fastInsert(KIO::UDSEntry::UDS_URL, url.toDisplayString());
0224 }
0225 
0226 KIO::WorkerResult LDAPProtocol::changeCheck(const LdapUrl &url)
0227 {
0228     LdapServer server;
0229     server.setUrl(url);
0230 
0231     if (mConnected) {
0232         if (server.host() != mServer.host() || server.port() != mServer.port() || server.baseDn() != mServer.baseDn() || server.user() != mServer.user()
0233             || server.bindDn() != mServer.bindDn() || server.realm() != mServer.realm() || server.password() != mServer.password()
0234             || server.timeLimit() != mServer.timeLimit() || server.sizeLimit() != mServer.sizeLimit() || server.version() != mServer.version()
0235             || server.security() != mServer.security() || server.auth() != mServer.auth() || server.mech() != mServer.mech()) {
0236             closeConnection();
0237             mServer = server;
0238             return openConnection();
0239         }
0240         return KIO::WorkerResult::pass();
0241     }
0242 
0243     mServer = server;
0244     return openConnection();
0245 }
0246 
0247 void LDAPProtocol::setHost(const QString &host, quint16 port, const QString &user, const QString &password)
0248 {
0249     if (mServer.host() != host || mServer.port() != port || mServer.user() != user || mServer.password() != password) {
0250         closeConnection();
0251     }
0252 
0253     mServer.host() = host;
0254     if (port > 0) {
0255         mServer.setPort(port);
0256     } else {
0257         struct servent *pse;
0258         if ((pse = getservbyname(mProtocol.constData(), "tcp")) == nullptr) {
0259             if (mProtocol == "ldaps") {
0260                 mServer.setPort(636);
0261             } else {
0262                 mServer.setPort(389);
0263             }
0264         } else {
0265             mServer.setPort(ntohs(pse->s_port));
0266         }
0267     }
0268     mServer.setUser(user);
0269     mServer.setPassword(password);
0270 
0271     qCDebug(KLDAP_LOG) << "setHost: " << host << " port: " << port << " user: " << user << " pass: [protected]";
0272 }
0273 
0274 KIO::WorkerResult LDAPProtocol::openConnection()
0275 {
0276     if (mConnected) {
0277         return KIO::WorkerResult::pass();
0278     }
0279 
0280     mConn.setServer(mServer);
0281     if (mConn.connect() != 0) {
0282         return KIO::WorkerResult::fail(ERR_CANNOT_CONNECT, mConn.connectionError());
0283     }
0284 
0285     mConnected = true;
0286 
0287     AuthInfo info;
0288     info.url.setScheme(QLatin1StringView(mProtocol));
0289     info.url.setHost(mServer.host());
0290     info.url.setPort(mServer.port());
0291     info.url.setUserName(mServer.user());
0292     info.caption = i18n("LDAP Login");
0293     info.comment = QString::fromLatin1(mProtocol) + QLatin1StringView("://") + mServer.host() + QLatin1Char(':') + QString::number(mServer.port());
0294     info.commentLabel = i18n("site:");
0295     info.username = mServer.auth() == LdapServer::SASL ? mServer.user() : mServer.bindDn();
0296     info.password = mServer.password();
0297     info.keepPassword = true;
0298     bool cached = checkCachedAuthentication(info);
0299 
0300     bool firstauth = true;
0301 
0302     while (true) {
0303         int retval = mOp.bind_s();
0304         if (retval == 0) {
0305             break;
0306         }
0307         if (retval == KLDAP_INVALID_CREDENTIALS || retval == KLDAP_INSUFFICIENT_ACCESS || retval == KLDAP_INAPPROPRIATE_AUTH
0308             || retval == KLDAP_UNWILLING_TO_PERFORM) {
0309             if (firstauth && cached) {
0310                 if (mServer.auth() == LdapServer::SASL) {
0311                     mServer.setUser(info.username);
0312                 } else {
0313                     mServer.setBindDn(info.username);
0314                 }
0315                 mServer.setPassword(info.password);
0316                 mConn.setServer(mServer);
0317                 cached = false;
0318             } else {
0319                 const int errorCode = firstauth ? openPasswordDialog(info) : openPasswordDialog(info, i18n("Invalid authorization information."));
0320                 if (!errorCode) {
0321                     if (info.keepPassword) { // user asked password be save/remembered
0322                         cacheAuthentication(info);
0323                     }
0324                 } else {
0325                     closeConnection();
0326                     if (errorCode == ERR_USER_CANCELED) {
0327                         return KIO::WorkerResult::fail(ERR_USER_CANCELED, i18n("LDAP connection canceled."));
0328                     }
0329                     return KIO::WorkerResult::fail(errorCode, QString());
0330                 }
0331                 if (mServer.auth() == LdapServer::SASL) {
0332                     mServer.setUser(info.username);
0333                 } else {
0334                     mServer.setBindDn(info.username);
0335                 }
0336                 mServer.setPassword(info.password);
0337                 firstauth = false;
0338                 mConn.setServer(mServer);
0339             }
0340         } else {
0341             const KIO::WorkerResult errorResult = LDAPErr(retval);
0342             // TODO: ensure that LDAPErr(retval) always closes the connection, avoiding the explicit call
0343             closeConnection();
0344             return errorResult;
0345         }
0346     }
0347 
0348     qCDebug(KLDAP_LOG) << "connected!";
0349     return KIO::WorkerResult::pass();
0350 }
0351 
0352 void LDAPProtocol::closeConnection()
0353 {
0354     if (mConnected) {
0355         mConn.close();
0356     }
0357     mConnected = false;
0358 
0359     qCDebug(KLDAP_LOG) << "connection closed!";
0360 }
0361 
0362 /**
0363  * Get the information contained in the URL.
0364  */
0365 KIO::WorkerResult LDAPProtocol::get(const QUrl &_url)
0366 {
0367     qCDebug(KLDAP_LOG) << "get(" << _url << ")";
0368 
0369     LdapUrl usrc(_url);
0370 
0371     const KIO::WorkerResult checkResult = changeCheck(usrc);
0372     if (!checkResult.success()) {
0373         return checkResult;
0374     }
0375 
0376     LdapControls serverctrls;
0377     LdapControls clientctrls;
0378     controlsFromMetaData(serverctrls, clientctrls);
0379     if (mServer.pageSize()) {
0380         LdapControls ctrls = serverctrls;
0381         ctrls.append(LdapControl::createPageControl(mServer.pageSize()));
0382         qCDebug(KLDAP_LOG) << "page size: " << mServer.pageSize();
0383         mOp.setServerControls(ctrls);
0384     } else {
0385         mOp.setServerControls(serverctrls);
0386     }
0387     mOp.setClientControls(clientctrls);
0388     int id;
0389     if ((id = mOp.search(usrc.dn(), usrc.scope(), usrc.filter(), usrc.attributes())) == -1) {
0390         return LDAPErr();
0391     }
0392 
0393     // tell the mimetype
0394     mimeType(QStringLiteral("text/plain"));
0395     // collect the result
0396     // QByteArray result;
0397     filesize_t processed_size = 0;
0398 
0399     int ret;
0400     while (true) {
0401         ret = mOp.waitForResult(id, -1);
0402         if (ret == -1 || mConn.ldapErrorCode() != KLDAP_SUCCESS) {
0403             return LDAPErr();
0404         }
0405         qCDebug(KLDAP_LOG) << " ldap_result: " << ret;
0406         if (ret == LdapOperation::RES_SEARCH_RESULT) {
0407             if (mServer.pageSize()) {
0408                 QByteArray cookie;
0409                 int estsize = -1;
0410                 for (int i = 0; i < mOp.controls().count(); ++i) {
0411                     qCDebug(KLDAP_LOG) << " control oid: " << mOp.controls().at(i).oid();
0412                     estsize = mOp.controls().at(i).parsePageControl(cookie);
0413                     if (estsize != -1) {
0414                         break;
0415                     }
0416                 }
0417                 qCDebug(KLDAP_LOG) << " estimated size: " << estsize;
0418                 if (estsize != -1 && !cookie.isEmpty()) {
0419                     LdapControls ctrls{serverctrls}; // clazy:exclude=container-inside-loop
0420                     qCDebug(KLDAP_LOG) << "page size: " << mServer.pageSize() << " estimated size: " << estsize;
0421                     ctrls.append(LdapControl::createPageControl(mServer.pageSize(), cookie));
0422                     mOp.setServerControls(ctrls);
0423                     if ((id = mOp.search(usrc.dn(), usrc.scope(), usrc.filter(), usrc.attributes())) == -1) {
0424                         return LDAPErr();
0425                     }
0426                     continue;
0427                 }
0428             }
0429             break;
0430         }
0431         if (ret != LdapOperation::RES_SEARCH_ENTRY) {
0432             continue;
0433         }
0434 
0435         QByteArray entry = mOp.object().toString().toUtf8() + '\n';
0436         processed_size += entry.size();
0437         data(entry);
0438         processedSize(processed_size);
0439     }
0440 
0441     totalSize(processed_size);
0442 
0443     // tell we are finished
0444     data(QByteArray());
0445     return KIO::WorkerResult::pass();
0446 }
0447 
0448 /**
0449  * Test if the url contains a directory or a file.
0450  */
0451 KIO::WorkerResult LDAPProtocol::stat(const QUrl &_url)
0452 {
0453     qCDebug(KLDAP_LOG) << "stat(" << _url << ")";
0454 
0455     LdapUrl usrc(_url);
0456 
0457     const KIO::WorkerResult checkResult = changeCheck(usrc);
0458     if (!checkResult.success()) {
0459         return checkResult;
0460     }
0461 
0462     int ret;
0463     int id;
0464     // look how many entries match
0465     const QStringList saveatt = usrc.attributes();
0466     QStringList att{QStringLiteral("dn")};
0467 
0468     if ((id = mOp.search(usrc.dn(), usrc.scope(), usrc.filter(), att)) == -1) {
0469         return LDAPErr();
0470     }
0471 
0472     qCDebug(KLDAP_LOG) << "stat() getting result";
0473     do {
0474         ret = mOp.waitForResult(id, -1);
0475         if (ret == -1 || mConn.ldapErrorCode() != KLDAP_SUCCESS) {
0476             return LDAPErr();
0477         }
0478         if (ret == LdapOperation::RES_SEARCH_RESULT) {
0479             return KIO::WorkerResult::fail(ERR_DOES_NOT_EXIST, _url.toDisplayString());
0480         }
0481     } while (ret != LdapOperation::RES_SEARCH_ENTRY);
0482 
0483     mOp.abandon(id);
0484 
0485     usrc.setAttributes(saveatt);
0486 
0487     UDSEntry uds;
0488     bool critical;
0489     LDAPEntry2UDSEntry(usrc.dn(), uds, usrc, usrc.extension(QStringLiteral("x-dir"), critical) != QLatin1StringView("base"));
0490 
0491     statEntry(uds);
0492     // we are done
0493     return KIO::WorkerResult::pass();
0494 }
0495 
0496 /**
0497  * Deletes one entry;
0498  */
0499 KIO::WorkerResult LDAPProtocol::del(const QUrl &_url, bool)
0500 {
0501     qCDebug(KLDAP_LOG) << "del(" << _url << ")";
0502 
0503     LdapUrl usrc(_url);
0504 
0505     const KIO::WorkerResult checkResult = changeCheck(usrc);
0506     if (!checkResult.success()) {
0507         return checkResult;
0508     }
0509 
0510     LdapControls serverctrls;
0511     LdapControls clientctrls;
0512     controlsFromMetaData(serverctrls, clientctrls);
0513     mOp.setServerControls(serverctrls);
0514     mOp.setClientControls(clientctrls);
0515 
0516     qCDebug(KLDAP_LOG) << " del: " << usrc.dn().toString().toUtf8();
0517     int id;
0518     int ret;
0519 
0520     if ((id = mOp.del(usrc.dn())) == -1) {
0521         return LDAPErr();
0522     }
0523     ret = mOp.waitForResult(id, -1);
0524     if (ret == -1 || mConn.ldapErrorCode() != KLDAP_SUCCESS) {
0525         return LDAPErr();
0526     }
0527 
0528     return KIO::WorkerResult::pass();
0529 }
0530 
0531 KIO::WorkerResult LDAPProtocol::put(const QUrl &_url, int, KIO::JobFlags flags)
0532 {
0533     qCDebug(KLDAP_LOG) << "put(" << _url << ")";
0534 
0535     LdapUrl usrc(_url);
0536 
0537     const KIO::WorkerResult checkResult = changeCheck(usrc);
0538     if (!checkResult.success()) {
0539         return checkResult;
0540     }
0541 
0542     LdapControls serverctrls;
0543     LdapControls clientctrls;
0544     controlsFromMetaData(serverctrls, clientctrls);
0545     mOp.setServerControls(serverctrls);
0546     mOp.setClientControls(clientctrls);
0547 
0548     LdapObject addObject;
0549     LdapOperation::ModOps modops;
0550     QByteArray buffer;
0551     int result = 0;
0552     Ldif::ParseValue ret;
0553     Ldif ldif;
0554     ret = Ldif::MoreData;
0555     int ldaperr;
0556 
0557     do {
0558         if (ret == Ldif::MoreData) {
0559             dataReq(); // Request for data
0560             result = readData(buffer);
0561             ldif.setLdif(buffer);
0562         }
0563         if (result < 0) {
0564             // error
0565             return KIO::WorkerResult::fail();
0566         }
0567         if (result == 0) {
0568             qCDebug(KLDAP_LOG) << "EOF!";
0569             ldif.endLdif();
0570         }
0571         do {
0572             ret = ldif.nextItem();
0573             qCDebug(KLDAP_LOG) << "nextitem: " << ret;
0574 
0575             switch (ret) {
0576             case Ldif::None:
0577             case Ldif::NewEntry:
0578             case Ldif::MoreData:
0579                 break;
0580             case Ldif::EndEntry:
0581                 ldaperr = KLDAP_SUCCESS;
0582                 switch (ldif.entryType()) {
0583                 case Ldif::Entry_None:
0584                     return KIO::WorkerResult::fail(ERR_INTERNAL, i18n("The Ldif parser failed."));
0585                 case Ldif::Entry_Del:
0586                     qCDebug(KLDAP_LOG) << "kio_ldap_del";
0587                     ldaperr = mOp.del_s(ldif.dn());
0588                     break;
0589                 case Ldif::Entry_Modrdn:
0590                     qCDebug(KLDAP_LOG) << "kio_ldap_modrdn olddn:" << ldif.dn().toString() << " newRdn: " << ldif.newRdn()
0591                                        << " newSuperior: " << ldif.newSuperior() << " deloldrdn: " << ldif.delOldRdn();
0592                     ldaperr = mOp.rename_s(ldif.dn(), ldif.newRdn(), ldif.newSuperior(), ldif.delOldRdn());
0593                     break;
0594                 case Ldif::Entry_Mod:
0595                     qCDebug(KLDAP_LOG) << "kio_ldap_mod";
0596                     ldaperr = mOp.modify_s(ldif.dn(), modops);
0597                     modops.clear();
0598                     break;
0599                 case Ldif::Entry_Add:
0600                     qCDebug(KLDAP_LOG) << "kio_ldap_add " << ldif.dn().toString();
0601                     addObject.setDn(ldif.dn());
0602                     ldaperr = mOp.add_s(addObject);
0603                     if (ldaperr == KLDAP_ALREADY_EXISTS && (flags & KIO::Overwrite)) {
0604                         qCDebug(KLDAP_LOG) << ldif.dn().toString() << " already exists, delete first";
0605                         ldaperr = mOp.del_s(ldif.dn());
0606                         if (ldaperr == KLDAP_SUCCESS) {
0607                             ldaperr = mOp.add_s(addObject);
0608                         }
0609                     }
0610                     addObject.clear();
0611                     break;
0612                 }
0613                 if (ldaperr != KLDAP_SUCCESS) {
0614                     qCDebug(KLDAP_LOG) << "put ldap error: " << ldaperr;
0615                     return LDAPErr(ldaperr);
0616                 }
0617                 break;
0618             case Ldif::Item:
0619                 switch (ldif.entryType()) {
0620                 case Ldif::Entry_Mod: {
0621                     LdapOperation::ModOp op;
0622                     op.type = LdapOperation::Mod_None;
0623                     switch (ldif.modType()) {
0624                     case Ldif::Mod_None:
0625                         op.type = LdapOperation::Mod_None;
0626                         break;
0627                     case Ldif::Mod_Add:
0628                         op.type = LdapOperation::Mod_Add;
0629                         break;
0630                     case Ldif::Mod_Replace:
0631                         op.type = LdapOperation::Mod_Replace;
0632                         break;
0633                     case Ldif::Mod_Del:
0634                         op.type = LdapOperation::Mod_Del;
0635                         break;
0636                     }
0637                     op.attr = ldif.attr();
0638                     if (!ldif.value().isNull()) {
0639                         op.values.append(ldif.value());
0640                     }
0641                     modops.append(op);
0642                     break;
0643                 }
0644                 case Ldif::Entry_Add:
0645                     if (!ldif.value().isEmpty()) {
0646                         addObject.addValue(ldif.attr(), ldif.value());
0647                     }
0648                     break;
0649                 default:
0650                     return KIO::WorkerResult::fail(ERR_INTERNAL, i18n("The Ldif parser failed."));
0651                 }
0652                 break;
0653             case Ldif::Control: {
0654                 LdapControl control;
0655                 control.setControl(ldif.oid(), ldif.value(), ldif.isCritical());
0656                 serverctrls.append(control);
0657                 mOp.setServerControls(serverctrls);
0658                 break;
0659             }
0660             case Ldif::Err:
0661                 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, i18n("Invalid Ldif file in line %1.", ldif.lineNumber()));
0662             }
0663         } while (ret != Ldif::MoreData);
0664     } while (result > 0);
0665 
0666     return KIO::WorkerResult::pass();
0667 }
0668 
0669 /**
0670  * List the contents of a directory.
0671  */
0672 KIO::WorkerResult LDAPProtocol::listDir(const QUrl &_url)
0673 {
0674     QStringList att;
0675     LdapUrl usrc(_url);
0676 
0677     qCDebug(KLDAP_LOG) << "listDir(" << _url << ")";
0678 
0679     const KIO::WorkerResult checkResult = changeCheck(usrc);
0680     if (!checkResult.success()) {
0681         return checkResult;
0682     }
0683 
0684     LdapUrl usrc2 = usrc;
0685 
0686     const QStringList saveatt = usrc.attributes();
0687     bool critical = true;
0688     bool isSub = (usrc.extension(QStringLiteral("x-dir"), critical) == QLatin1StringView("sub"));
0689     // look up the entries
0690     if (isSub) {
0691         att.append(QStringLiteral("dn"));
0692         usrc.setAttributes(att);
0693     }
0694     if (_url.query().isEmpty()) {
0695         usrc.setScope(LdapUrl::One);
0696     }
0697     int id;
0698 
0699     if ((id = mOp.search(usrc.dn(), usrc.scope(), usrc.filter(), usrc.attributes())) == -1) {
0700         return LDAPErr();
0701     }
0702 
0703     usrc.setAttributes(QStringList() << QLatin1StringView(""));
0704     usrc.setExtension(QStringLiteral("x-dir"), QStringLiteral("base"));
0705     // publish the results
0706     UDSEntry uds;
0707 
0708     int ret2;
0709     int id2;
0710     unsigned long total = 0;
0711 
0712     int ret;
0713     while (true) {
0714         ret = mOp.waitForResult(id, -1);
0715         if (ret == -1 || mConn.ldapErrorCode() != KLDAP_SUCCESS) {
0716             return LDAPErr();
0717         }
0718         if (ret == LdapOperation::RES_SEARCH_RESULT) {
0719             break;
0720         }
0721         if (ret != LdapOperation::RES_SEARCH_ENTRY) {
0722             continue;
0723         }
0724         qCDebug(KLDAP_LOG) << " ldap_result: " << ret;
0725 
0726         total++;
0727         uds.clear();
0728 
0729         LDAPEntry2UDSEntry(mOp.object().dn(), uds, usrc);
0730         listEntry(uds);
0731         //      processedSize( total );
0732         qCDebug(KLDAP_LOG) << " total: " << total << " " << usrc.toDisplayString();
0733 
0734         // publish the sub-directories (if dirmode==sub)
0735         if (isSub) {
0736             LdapDN dn = mOp.object().dn();
0737             usrc2.setDn(dn);
0738             usrc2.setScope(LdapUrl::One);
0739             usrc2.setAttributes(saveatt);
0740             usrc2.setFilter(usrc.filter());
0741             qCDebug(KLDAP_LOG) << "search2 " << dn.toString();
0742             if ((id2 = mOp.search(dn, LdapUrl::One, QString(), att)) != -1) {
0743                 while (true) {
0744                     qCDebug(KLDAP_LOG) << " next result ";
0745                     ret2 = mOp.waitForResult(id2, -1);
0746                     if (ret2 == -1 || ret2 == LdapOperation::RES_SEARCH_RESULT) {
0747                         break;
0748                     }
0749                     if (ret2 == LdapOperation::RES_SEARCH_ENTRY) {
0750                         LDAPEntry2UDSEntry(dn, uds, usrc2, true);
0751                         listEntry(uds);
0752                         total++;
0753                         mOp.abandon(id2);
0754                         break;
0755                     }
0756                 }
0757             }
0758         }
0759     }
0760 
0761     //  totalSize( total );
0762 
0763     uds.clear();
0764     // we are done
0765     return KIO::WorkerResult::pass();
0766 }
0767 
0768 #include "kio_ldap.moc"