1 | /* |
2 | This file is part of libkldap. |
3 | Copyright (c) 2004-2006 Szombathelyi György <gyurco@freemail.hu> |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Library General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2 of the License, or (at your option) any later version. |
9 | |
10 | This library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Library General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Library General Public License |
16 | along with this library; see the file COPYING.LIB. If not, write to |
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "ldapsearch.h" |
22 | #include "ldapdn.h" |
23 | #include "ldapdefs.h" |
24 | |
25 | #include <QtCore/QEventLoop> |
26 | #include <QtCore/QTimer> |
27 | |
28 | #include <kdebug.h> |
29 | #include <KLocalizedString> |
30 | using namespace KLDAP; |
31 | |
32 | //blocking the GUI for xxx milliseconds |
33 | #define LDAPSEARCH_BLOCKING_TIMEOUT 10 |
34 | |
35 | class LdapSearch::Private |
36 | { |
37 | public: |
38 | Private( LdapSearch *parent ) |
39 | : mParent( parent ) |
40 | { |
41 | } |
42 | |
43 | void result(); |
44 | bool connect(); |
45 | void closeConnection(); |
46 | bool startSearch( const LdapDN &base, LdapUrl::Scope scope, |
47 | const QString &filter, const QStringList &attributes, |
48 | int pagesize, int count ); |
49 | |
50 | LdapSearch *mParent; |
51 | LdapConnection *mConn; |
52 | LdapOperation mOp; |
53 | bool mOwnConnection, mAbandoned; |
54 | int mId, mPageSize; |
55 | LdapDN mBase; |
56 | QString mFilter; |
57 | QStringList mAttributes; |
58 | LdapUrl::Scope mScope; |
59 | |
60 | QString mErrorString; |
61 | int mError; |
62 | int mCount, mMaxCount; |
63 | bool mFinished; |
64 | }; |
65 | |
66 | void LdapSearch::Private::result() |
67 | { |
68 | if ( mAbandoned ) { |
69 | mOp.abandon( mId ); |
70 | return; |
71 | } |
72 | int res = mOp.waitForResult( mId, LDAPSEARCH_BLOCKING_TIMEOUT ); |
73 | |
74 | kDebug() << "LDAP result:" << res; |
75 | |
76 | if ( res != 0 && |
77 | ( res == -1 || |
78 | ( mConn->ldapErrorCode() != KLDAP_SUCCESS && |
79 | mConn->ldapErrorCode() != KLDAP_SASL_BIND_IN_PROGRESS ) ) ) { |
80 | //error happened, but no timeout |
81 | mError = mConn->ldapErrorCode(); |
82 | mErrorString = mConn->ldapErrorString(); |
83 | emit mParent->result( mParent ); |
84 | return; |
85 | } |
86 | |
87 | //binding |
88 | if ( res == LdapOperation::RES_BIND ) { |
89 | |
90 | QByteArray servercc; |
91 | servercc = mOp.serverCred(); |
92 | |
93 | kDebug() << "LdapSearch RES_BIND" ; |
94 | if ( mConn->ldapErrorCode() == KLDAP_SUCCESS ) { //bind succeeded |
95 | kDebug() << "bind succeeded" ; |
96 | LdapControls savedctrls = mOp.serverControls(); |
97 | if ( mPageSize ) { |
98 | LdapControls ctrls = savedctrls; |
99 | LdapControl::insert( ctrls, LdapControl::createPageControl( mPageSize ) ); |
100 | mOp.setServerControls( ctrls ); |
101 | } |
102 | |
103 | mId = mOp.search( mBase, mScope, mFilter, mAttributes ); |
104 | mOp.setServerControls( savedctrls ); |
105 | } else { //next bind step |
106 | kDebug() << "bind next step" ; |
107 | mId = mOp.bind( servercc ); |
108 | } |
109 | if ( mId < 0 ) { |
110 | if ( mId == KLDAP_SASL_ERROR ) { |
111 | mError = mId; |
112 | mErrorString = mConn->saslErrorString(); |
113 | } else { |
114 | mError = mConn->ldapErrorCode(); |
115 | mErrorString = mConn->ldapErrorString(); |
116 | } |
117 | emit mParent->result( mParent ); |
118 | return; |
119 | } |
120 | QTimer::singleShot( 0, mParent, SLOT(result()) ); |
121 | return; |
122 | } |
123 | |
124 | //End of entries |
125 | if ( res == LdapOperation::RES_SEARCH_RESULT ) { |
126 | if ( mPageSize ) { |
127 | QByteArray cookie; |
128 | int estsize = -1; |
129 | const int numberOfControls( mOp.controls().count() ); |
130 | for ( int i = 0; i < numberOfControls; ++i ) { |
131 | estsize = mOp.controls()[i].parsePageControl( cookie ); |
132 | if ( estsize != -1 ) { |
133 | break; |
134 | } |
135 | } |
136 | kDebug() << " estimated size:" << estsize; |
137 | if ( estsize != -1 && !cookie.isEmpty() ) { |
138 | LdapControls ctrls, savedctrls; |
139 | savedctrls = mOp.serverControls(); |
140 | ctrls = savedctrls; |
141 | LdapControl::insert( ctrls, LdapControl::createPageControl ( mPageSize, cookie ) ); |
142 | mOp.setServerControls( ctrls ); |
143 | mId = mOp.search( mBase, mScope, mFilter, mAttributes ); |
144 | mOp.setServerControls( savedctrls ); |
145 | if ( mId == -1 ) { |
146 | mError = mConn->ldapErrorCode(); |
147 | mErrorString = mConn->ldapErrorString(); |
148 | emit mParent->result( mParent ); |
149 | return; |
150 | } |
151 | //continue with the next page |
152 | QTimer::singleShot( 0, mParent, SLOT(result()) ); |
153 | return; |
154 | } |
155 | } |
156 | mFinished = true; |
157 | emit mParent->result( mParent ); |
158 | return; |
159 | } |
160 | |
161 | //Found an entry |
162 | if ( res == LdapOperation::RES_SEARCH_ENTRY ) { |
163 | emit mParent->data( mParent, mOp.object() ); |
164 | mCount++; |
165 | } |
166 | |
167 | //If not reached the requested entries, continue |
168 | if ( mMaxCount <= 0 || mCount < mMaxCount ) { |
169 | QTimer::singleShot( 0, mParent, SLOT(result()) ); |
170 | } |
171 | //If reached the requested entries, indicate it |
172 | if ( mMaxCount > 0 && mCount == mMaxCount ) { |
173 | kDebug() << mCount << " entries reached" ; |
174 | emit mParent->result( mParent ); |
175 | } |
176 | } |
177 | |
178 | bool LdapSearch::Private::connect() |
179 | { |
180 | int ret = mConn->connect(); |
181 | if ( ret != KLDAP_SUCCESS ) { |
182 | mError = ret; |
183 | mErrorString = mConn->connectionError(); |
184 | closeConnection(); |
185 | return false; |
186 | } |
187 | return true; |
188 | } |
189 | |
190 | void LdapSearch::Private::closeConnection() |
191 | { |
192 | if ( mOwnConnection && mConn ) { |
193 | delete mConn; |
194 | mConn = 0; |
195 | } |
196 | } |
197 | |
198 | //This starts the real job |
199 | bool LdapSearch::Private::startSearch( const LdapDN &base, LdapUrl::Scope scope, |
200 | const QString &filter, |
201 | const QStringList &attributes, int pagesize, int count ) |
202 | { |
203 | kDebug() << "search: base=" << base.toString() << "scope=" << (int)scope |
204 | << "filter=" << filter << "attributes=" << attributes |
205 | << "pagesize=" << pagesize; |
206 | mAbandoned = false; |
207 | mError = 0; |
208 | mErrorString.clear(); |
209 | mOp.setConnection( *mConn ); |
210 | mPageSize = pagesize; |
211 | mBase = base; |
212 | mScope = scope; |
213 | mFilter = filter; |
214 | mAttributes = attributes; |
215 | mMaxCount = count; |
216 | mCount = 0; |
217 | mFinished = false; |
218 | |
219 | LdapControls savedctrls = mOp.serverControls(); |
220 | if ( pagesize ) { |
221 | LdapControls ctrls = savedctrls; |
222 | mConn->setOption( 0x0008, NULL ); // Disable referals or paging won't work |
223 | LdapControl::insert( ctrls, LdapControl::createPageControl( pagesize ) ); |
224 | mOp.setServerControls( ctrls ); |
225 | } |
226 | |
227 | mId = mOp.bind(); |
228 | if ( mId < 0 ) { |
229 | if ( mId == KLDAP_SASL_ERROR ) { |
230 | mError = mId; |
231 | mErrorString = mConn->saslErrorString(); |
232 | } else { |
233 | mError = mConn->ldapErrorCode(); |
234 | mErrorString = mConn->ldapErrorString(); |
235 | if ( mError == -1 && mErrorString.isEmpty() ) { |
236 | mErrorString = i18n( "Cannot access to server. Please reconfigure it." ); |
237 | } |
238 | } |
239 | return false; |
240 | } |
241 | kDebug() << "startSearch msg id=" << mId; |
242 | |
243 | //maybe do this with threads?- need thread-safe client libs!!! |
244 | QTimer::singleShot( 0, mParent, SLOT(result()) ); |
245 | |
246 | return true; |
247 | } |
248 | |
249 | /////////////////////////////////////////////// |
250 | |
251 | LdapSearch::LdapSearch() |
252 | : d( new Private( this ) ) |
253 | { |
254 | d->mOwnConnection = true; |
255 | d->mConn = 0; |
256 | } |
257 | |
258 | LdapSearch::LdapSearch( LdapConnection &connection ) |
259 | : d( new Private( this ) ) |
260 | { |
261 | d->mOwnConnection = false; |
262 | d->mConn = &connection; |
263 | } |
264 | |
265 | LdapSearch::~LdapSearch() |
266 | { |
267 | d->closeConnection(); |
268 | delete d; |
269 | } |
270 | |
271 | void LdapSearch::setConnection( LdapConnection &connection ) |
272 | { |
273 | d->closeConnection(); |
274 | d->mOwnConnection = false; |
275 | d->mConn = &connection; |
276 | } |
277 | |
278 | void LdapSearch::setClientControls( const LdapControls &ctrls ) |
279 | { |
280 | d->mOp.setClientControls( ctrls ); |
281 | } |
282 | |
283 | void LdapSearch::setServerControls( const LdapControls &ctrls ) |
284 | { |
285 | d->mOp.setServerControls( ctrls ); |
286 | } |
287 | |
288 | bool LdapSearch::search( const LdapServer &server, |
289 | const QStringList &attributes, int count ) |
290 | { |
291 | if ( d->mOwnConnection ) { |
292 | d->closeConnection(); |
293 | d->mConn = new LdapConnection( server ); |
294 | if ( !d->connect() ) { |
295 | return false; |
296 | } |
297 | } |
298 | return d->startSearch( server.baseDn(), server.scope(), server.filter(), |
299 | attributes, server.pageSize(), count ); |
300 | } |
301 | |
302 | bool LdapSearch::search( const LdapUrl &url, int count ) |
303 | { |
304 | if ( d->mOwnConnection ) { |
305 | d->closeConnection(); |
306 | d->mConn = new LdapConnection( url ); |
307 | if ( !d->connect() ) { |
308 | return false; |
309 | } |
310 | } |
311 | bool critical = true; |
312 | int pagesize = url.extension( QLatin1String( "x-pagesize" ), critical ).toInt(); |
313 | return d->startSearch( url.dn(), url.scope(), url.filter(), |
314 | url.attributes(), pagesize, count ); |
315 | } |
316 | |
317 | bool LdapSearch::search( const LdapDN &base, LdapUrl::Scope scope, |
318 | const QString &filter, const QStringList &attributes, |
319 | int pagesize, int count ) |
320 | { |
321 | Q_ASSERT( !d->mOwnConnection ); |
322 | return d->startSearch( base, scope, filter, attributes, pagesize, count ); |
323 | } |
324 | |
325 | void LdapSearch::continueSearch() |
326 | { |
327 | Q_ASSERT( !d->mFinished ); |
328 | d->mCount = 0; |
329 | QTimer::singleShot( 0, this, SLOT(result()) ); |
330 | } |
331 | |
332 | bool LdapSearch::isFinished() |
333 | { |
334 | return d->mFinished; |
335 | } |
336 | |
337 | void LdapSearch::abandon() |
338 | { |
339 | d->mAbandoned = true; |
340 | } |
341 | |
342 | int LdapSearch::error() const |
343 | { |
344 | return d->mError; |
345 | } |
346 | |
347 | QString LdapSearch::errorString() const |
348 | { |
349 | return d->mErrorString; |
350 | } |
351 | |
352 | #include "moc_ldapsearch.cpp" |
353 | |