File indexing completed on 2025-03-02 04:55:46
0001 /* 0002 This file is part of libkldap. 0003 SPDX-FileCopyrightText: 2004-2006 Szombathelyi György <gyurco@freemail.hu> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "ldapsearch.h" 0009 #include "ldapdefs.h" 0010 #include "ldapdn.h" 0011 0012 #include <QTimer> 0013 0014 #include "ldap_core_debug.h" 0015 #include <KLocalizedString> 0016 using namespace KLDAPCore; 0017 0018 // blocking the GUI for xxx milliseconds 0019 #define LDAPSEARCH_BLOCKING_TIMEOUT 10 0020 0021 class LdapSearchPrivate 0022 { 0023 public: 0024 explicit LdapSearchPrivate(LdapSearch *parent) 0025 : mParent(parent) 0026 { 0027 } 0028 0029 void result(); 0030 bool connect(); 0031 void closeConnection(); 0032 bool startSearch(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes, int pagesize, int count); 0033 0034 LdapSearch *const mParent; 0035 LdapConnection *mConn = nullptr; 0036 LdapOperation mOp; 0037 bool mOwnConnection = false; 0038 bool mAbandoned = false; 0039 int mId; 0040 int mPageSize; 0041 LdapDN mBase; 0042 QString mFilter; 0043 QStringList mAttributes; 0044 LdapUrl::Scope mScope; 0045 0046 QString mErrorString; 0047 int mError; 0048 int mCount; 0049 int mMaxCount; 0050 bool mFinished = false; 0051 }; 0052 0053 void LdapSearchPrivate::result() 0054 { 0055 if (mAbandoned) { 0056 mOp.abandon(mId); 0057 return; 0058 } 0059 const int res = mOp.waitForResult(mId, LDAPSEARCH_BLOCKING_TIMEOUT); 0060 0061 qCDebug(LDAP_LOG) << "LDAP result:" << res; 0062 0063 if (res != 0 && (res == -1 || (mConn->ldapErrorCode() != KLDAP_SUCCESS && mConn->ldapErrorCode() != KLDAP_SASL_BIND_IN_PROGRESS))) { 0064 // error happened, but no timeout 0065 mError = mConn->ldapErrorCode(); 0066 mErrorString = mConn->ldapErrorString(); 0067 Q_EMIT mParent->result(mParent); 0068 return; 0069 } 0070 0071 // binding 0072 if (res == LdapOperation::RES_BIND) { 0073 const QByteArray servercc = mOp.serverCred(); 0074 0075 qCDebug(LDAP_LOG) << "LdapSearch RES_BIND"; 0076 if (mConn->ldapErrorCode() == KLDAP_SUCCESS) { // bind succeeded 0077 qCDebug(LDAP_LOG) << "bind succeeded"; 0078 LdapControls savedctrls = mOp.serverControls(); 0079 if (mPageSize) { 0080 LdapControls ctrls = savedctrls; 0081 LdapControl::insert(ctrls, LdapControl::createPageControl(mPageSize)); 0082 mOp.setServerControls(ctrls); 0083 } 0084 0085 mId = mOp.search(mBase, mScope, mFilter, mAttributes); 0086 mOp.setServerControls(savedctrls); 0087 } else { // next bind step 0088 qCDebug(LDAP_LOG) << "bind next step"; 0089 mId = mOp.bind(servercc); 0090 } 0091 if (mId < 0) { 0092 if (mId == KLDAP_SASL_ERROR) { 0093 mError = mId; 0094 mErrorString = mConn->saslErrorString(); 0095 } else { 0096 mError = mConn->ldapErrorCode(); 0097 mErrorString = mConn->ldapErrorString(); 0098 } 0099 Q_EMIT mParent->result(mParent); 0100 return; 0101 } 0102 QTimer::singleShot(0, mParent, [this]() { 0103 result(); 0104 }); 0105 return; 0106 } 0107 0108 // End of entries 0109 if (res == LdapOperation::RES_SEARCH_RESULT) { 0110 if (mPageSize) { 0111 QByteArray cookie; 0112 int estsize = -1; 0113 const int numberOfControls(mOp.controls().count()); 0114 for (int i = 0; i < numberOfControls; ++i) { 0115 estsize = mOp.controls().at(i).parsePageControl(cookie); 0116 if (estsize != -1) { 0117 break; 0118 } 0119 } 0120 qCDebug(LDAP_LOG) << " estimated size:" << estsize; 0121 if (estsize != -1 && !cookie.isEmpty()) { 0122 LdapControls ctrls; 0123 LdapControls savedctrls; 0124 savedctrls = mOp.serverControls(); 0125 ctrls = savedctrls; 0126 LdapControl::insert(ctrls, LdapControl::createPageControl(mPageSize, cookie)); 0127 mOp.setServerControls(ctrls); 0128 mId = mOp.search(mBase, mScope, mFilter, mAttributes); 0129 mOp.setServerControls(savedctrls); 0130 if (mId == -1) { 0131 mError = mConn->ldapErrorCode(); 0132 mErrorString = mConn->ldapErrorString(); 0133 Q_EMIT mParent->result(mParent); 0134 return; 0135 } 0136 // continue with the next page 0137 QTimer::singleShot(0, mParent, [this]() { 0138 result(); 0139 }); 0140 return; 0141 } 0142 } 0143 mFinished = true; 0144 Q_EMIT mParent->result(mParent); 0145 return; 0146 } 0147 0148 // Found an entry 0149 if (res == LdapOperation::RES_SEARCH_ENTRY) { 0150 Q_EMIT mParent->data(mParent, mOp.object()); 0151 mCount++; 0152 } 0153 0154 // If not reached the requested entries, continue 0155 if (mMaxCount <= 0 || mCount < mMaxCount) { 0156 QTimer::singleShot(0, mParent, [this]() { 0157 result(); 0158 }); 0159 } 0160 // If reached the requested entries, indicate it 0161 if (mMaxCount > 0 && mCount == mMaxCount) { 0162 qCDebug(LDAP_LOG) << mCount << " entries reached"; 0163 Q_EMIT mParent->result(mParent); 0164 } 0165 } 0166 0167 bool LdapSearchPrivate::connect() 0168 { 0169 const int ret = mConn->connect(); 0170 if (ret != KLDAP_SUCCESS) { 0171 mError = ret; 0172 mErrorString = mConn->connectionError(); 0173 closeConnection(); 0174 return false; 0175 } 0176 return true; 0177 } 0178 0179 void LdapSearchPrivate::closeConnection() 0180 { 0181 if (mOwnConnection && mConn) { 0182 delete mConn; 0183 mConn = nullptr; 0184 } 0185 } 0186 0187 // This starts the real job 0188 bool LdapSearchPrivate::startSearch(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes, int pagesize, int count) 0189 { 0190 qCDebug(LDAP_LOG) << "search: base=" << base.toString() << "scope=" << static_cast<int>(scope) << "filter=" << filter << "attributes=" << attributes 0191 << "pagesize=" << pagesize; 0192 mAbandoned = false; 0193 mError = 0; 0194 mErrorString.clear(); 0195 mOp.setConnection(*mConn); 0196 mPageSize = pagesize; 0197 mBase = base; 0198 mScope = scope; 0199 mFilter = filter; 0200 mAttributes = attributes; 0201 mMaxCount = count; 0202 mCount = 0; 0203 mFinished = false; 0204 0205 LdapControls savedctrls = mOp.serverControls(); 0206 if (pagesize) { 0207 LdapControls ctrls = savedctrls; 0208 mConn->setOption(0x0008, nullptr); // Disable referals or paging won't work 0209 LdapControl::insert(ctrls, LdapControl::createPageControl(pagesize)); 0210 mOp.setServerControls(ctrls); 0211 } 0212 0213 mId = mOp.bind(); 0214 if (mId < 0) { 0215 if (mId == KLDAP_SASL_ERROR) { 0216 mError = mId; 0217 mErrorString = mConn->saslErrorString(); 0218 } else { 0219 mError = mConn->ldapErrorCode(); 0220 mErrorString = mConn->ldapErrorString(); 0221 if (mError == -1 && mErrorString.isEmpty()) { 0222 mErrorString = i18n("Cannot access to server. Please reconfigure it."); 0223 } 0224 } 0225 return false; 0226 } 0227 qCDebug(LDAP_LOG) << "startSearch msg id=" << mId; 0228 0229 // maybe do this with threads?- need thread-safe client libs!!! 0230 QTimer::singleShot(0, mParent, [this]() { 0231 result(); 0232 }); 0233 0234 return true; 0235 } 0236 0237 /////////////////////////////////////////////// 0238 0239 LdapSearch::LdapSearch() 0240 : d(new LdapSearchPrivate(this)) 0241 { 0242 d->mOwnConnection = true; 0243 d->mConn = nullptr; 0244 } 0245 0246 LdapSearch::LdapSearch(LdapConnection &connection) 0247 : d(new LdapSearchPrivate(this)) 0248 { 0249 d->mOwnConnection = false; 0250 d->mConn = &connection; 0251 } 0252 0253 LdapSearch::~LdapSearch() 0254 { 0255 d->closeConnection(); 0256 } 0257 0258 void LdapSearch::setConnection(LdapConnection &connection) 0259 { 0260 d->closeConnection(); 0261 d->mOwnConnection = false; 0262 d->mConn = &connection; 0263 } 0264 0265 void LdapSearch::setClientControls(const LdapControls &ctrls) 0266 { 0267 d->mOp.setClientControls(ctrls); 0268 } 0269 0270 void LdapSearch::setServerControls(const LdapControls &ctrls) 0271 { 0272 d->mOp.setServerControls(ctrls); 0273 } 0274 0275 bool LdapSearch::search(const LdapServer &server, const QStringList &attributes, int count) 0276 { 0277 if (d->mOwnConnection) { 0278 d->closeConnection(); 0279 d->mConn = new LdapConnection(server); 0280 if (!d->connect()) { 0281 return false; 0282 } 0283 } 0284 return d->startSearch(server.baseDn(), server.scope(), server.filter(), attributes, server.pageSize(), count); 0285 } 0286 0287 bool LdapSearch::search(const LdapUrl &url, int count) 0288 { 0289 if (d->mOwnConnection) { 0290 d->closeConnection(); 0291 d->mConn = new LdapConnection(url); 0292 if (!d->connect()) { 0293 return false; 0294 } 0295 } 0296 bool critical = true; 0297 const int pagesize = url.extension(QStringLiteral("x-pagesize"), critical).toInt(); 0298 return d->startSearch(url.dn(), url.scope(), url.filter(), url.attributes(), pagesize, count); 0299 } 0300 0301 bool LdapSearch::search(const LdapDN &base, LdapUrl::Scope scope, const QString &filter, const QStringList &attributes, int pagesize, int count) 0302 { 0303 Q_ASSERT(!d->mOwnConnection); 0304 return d->startSearch(base, scope, filter, attributes, pagesize, count); 0305 } 0306 0307 void LdapSearch::continueSearch() 0308 { 0309 Q_ASSERT(!d->mFinished); 0310 d->mCount = 0; 0311 QTimer::singleShot(0, this, [this]() { 0312 d->result(); 0313 }); 0314 } 0315 0316 bool LdapSearch::isFinished() 0317 { 0318 return d->mFinished; 0319 } 0320 0321 void LdapSearch::abandon() 0322 { 0323 d->mAbandoned = true; 0324 } 0325 0326 int LdapSearch::error() const 0327 { 0328 return d->mError; 0329 } 0330 0331 QString LdapSearch::errorString() const 0332 { 0333 return d->mErrorString; 0334 } 0335 0336 #include "moc_ldapsearch.cpp"