2 * Copyright (c) 2004 Szombathelyi György <gyurco@freemail.hu>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
22 #include <QCryptographicHash>
26 #include <kio/kntlm.h>
27 #include <kldap/ldapdefs.h>
28 #include <kldap/ldapdn.h>
29 #include <kldap/ldapconnection.h>
30 #include <kldap/ldapoperation.h>
32 #include "ku_userldap.h"
35 KU_UserLDAP::KU_UserLDAP(KU_PrefsBase
*cfg
) : KU_Users( cfg
)
39 if ( mCfg
->ldapssl() )
40 mUrl
.setProtocol("ldaps");
42 mUrl
.setProtocol("ldap");
44 mUrl
.setHost( mCfg
->ldaphost() );
45 mUrl
.setPort( mCfg
->ldapport() );
46 mUrl
.setDn( KLDAP::LdapDN( mCfg
->ldapuserbase() + ',' + mCfg
->ldapdn() ) );
47 if ( !mCfg
->ldapanon() ) {
48 mUrl
.setUser( mCfg
->ldapuser() );
49 mUrl
.setPass( mCfg
->ldappassword() );
50 QString binddn
= mCfg
->ldapbinddn();
51 if ( !binddn
.isEmpty() )
52 mUrl
.setExtension( "bindname",binddn
);
54 mUrl
.setFilter( mCfg
->ldapuserfilter() );
56 if ( mCfg
->ldaptls() ) mUrl
.setExtension( "x-tls", "" );
57 if ( mCfg
->ldapsasl() ) {
58 mUrl
.setExtension( "x-sasl", "" );
59 mUrl
.setExtension( "x-mech", mCfg
->ldapsaslmech() );
62 mUrl
.setScope(KLDAP::LdapUrl::One
);
63 mUrl
.setExtension("x-dir","base");
65 if ( mCfg
->ldaptimelimit() )
66 mUrl
.setExtension("x-timelimit",QString::number(mCfg
->ldaptimelimit()));
67 if ( mCfg
->ldapsizelimit() )
68 mUrl
.setExtension("x-sizelimit",QString::number(mCfg
->ldapsizelimit()));
69 if ( mCfg
->ldappagesize() )
70 mUrl
.setExtension("x-pagesize",QString::number(mCfg
->ldappagesize()));
72 caps
= Cap_Passwd
| Cap_Disable_POSIX
;
73 if ( mCfg
->ldapshadow() ) caps
|= Cap_Shadow
;
74 if ( mCfg
->ldapstructural() == KU_PrefsBase::EnumLdapstructural::inetOrgPerson
)
77 if ( mCfg
->ldapsam() ) {
79 domsid
= mCfg
->samdomsid();
83 KU_UserLDAP::~KU_UserLDAP()
87 void KU_UserLDAP::result( KLDAP::LdapSearch
*search
)
89 kDebug() << "LDAP result: " << search
->error() << " " << search
->errorString();
92 if ( search
->error() ) {
93 mErrorString
= KLDAP::LdapConnection::errorString(search
->error());
94 mErrorDetails
= search
->errorString();
101 void KU_UserLDAP::data( KLDAP::LdapSearch
*, const KLDAP::LdapObject
& data
)
104 QStringList objectclasses
;
106 KLDAP::LdapAttrMap attrs
= data
.attributes();
107 for ( KLDAP::LdapAttrMap::ConstIterator it
= attrs
.constBegin(); it
!= attrs
.constEnd(); ++it
) {
108 QString name
= it
.key().toLower();
109 if ( name
== "objectclass" ) {
110 for ( KLDAP::LdapAttrValue::ConstIterator it2
= (*it
).constBegin(); it2
!= (*it
).constEnd(); ++it2
) {
111 if ( (*it2
).toLower() == "posixaccount" )
112 user
.setCaps( user
.getCaps() | KU_User::Cap_POSIX
);
113 else if ( (*it2
).toLower() == "sambasamaccount" )
114 user
.setCaps( user
.getCaps() | KU_User::Cap_Samba
);
115 else if ( (*it2
).toLower() != "inetorgperson" &&
116 (*it2
).toLower() != "shadowaccount" &&
117 (*it2
).toLower() != "account" )
118 objectclasses
.append( (*it2
) );
123 KLDAP::LdapAttrValue values
= (*it
);
124 if ( values
.isEmpty() ) continue;
125 QString val
= QString::fromUtf8( values
.first(), values
.first().size() );
126 if ( name
== "uidnumber" )
127 user
.setUID( val
.toLong() );
128 else if ( name
== "gidnumber" )
129 user
.setGID( val
.toLong() );
130 else if ( name
== "uid" || name
== "userid" )
132 else if ( name
== "sn" )
133 user
.setSurname( val
);
134 else if ( name
== "mail" )
135 user
.setEmail( val
);
136 else if ( name
== "homedirectory" )
137 user
.setHomeDir( val
);
138 else if ( name
== "loginshell" )
139 user
.setShell( val
);
140 else if ( name
== "postaladdress" )
141 user
.setAddress( val
);
142 else if ( name
== "telephonenumber" ) {
143 user
.setOffice1( val
);
144 if ( values
.size() > 1 )
145 user
.setOffice2( QString::fromUtf8( values
[1], values
[1].size() ) );
146 } else if ( name
== "gecos" ) {
147 QString name
, f1
, f2
, f3
;
148 parseGecos( values
.first(), name
, f1
, f2
, f3
);
149 if ( user
.getFullName().isEmpty() ) user
.setFullName( val
);
150 if ( user
.getOffice1().isEmpty() ) user
.setOffice1( f1
);
151 if ( user
.getOffice2().isEmpty() ) user
.setOffice2( f1
);
152 if ( user
.getAddress().isEmpty() ) user
.setAddress( f1
);
153 } else if ( name
== "cn" ) {
154 if ( user
.getFullName().isEmpty() || mCfg
->ldapcnfullname() )
155 user
.setFullName( val
);
156 if ( user
.getName().isEmpty() )
158 } else if ( name
== "displayname" ) {
159 user
.setFullName( val
);
160 } else if ( name
== "userpassword" ) {
161 if ( !val
.isEmpty() ) user
.setDisabled( false );
163 } else if ( name
== "shadowlastchange" ) {
164 if ( user
.getLastChange() == 0 ) //sambapwdlastset is more precise
165 user
.setLastChange( daysToTime( val
.toLong() ) );
166 } else if ( name
== "shadowmin" )
167 user
.setMin( val
.toInt() );
168 else if ( name
== "shadowmax" )
169 user
.setMax( val
.toLong() );
170 else if ( name
== "shadowwarning" )
171 user
.setWarn( val
.toLong() );
172 else if ( name
== "shadowinactive" )
173 user
.setInactive( val
.toLong() );
174 else if ( name
== "shadowexpire" )
175 user
.setExpire( val
.toLong() );
176 else if ( name
== "shadowflag" )
177 user
.setFlag( val
.toLong() );
178 else if ( name
== "sambaacctflags" ) {
179 if ( !val
.contains( 'D' ) ) user
.setDisabled( false );
180 } else if ( name
== "sambasid" )
182 else if ( name
== "sambaprimarygroupsid" )
183 user
.setPGSID( val
);
184 else if ( name
== "sambalmpassword" )
185 user
.setLMPwd( val
);
186 else if ( name
== "sambantpassword" )
187 user
.setNTPwd( val
);
188 else if ( name
== "sambahomepath" )
189 user
.setHomePath( val
);
190 else if ( name
== "sambahomedrive" )
191 user
.setHomeDrive( val
);
192 else if ( name
== "sambalogonscript" )
193 user
.setLoginScript( val
);
194 else if ( name
== "sambaprofilepath" )
195 user
.setProfilePath( val
);
196 else if ( name
== "sambauserworkstations" )
197 user
.setWorkstations( val
);
198 else if ( name
== "sambadomainname" )
199 user
.setDomain( val
);
200 else if ( name
== "sambapwdlastset" )
201 user
.setLastChange( val
.toLong() );
202 //these new attributes introduced around samba 3.0.6
203 else if ( name
== "sambapasswordhistory" || name
== "sambalogonhours" )
208 kDebug() << "new user: " << user
.getName();
209 if ( !objectclasses
.isEmpty() ) {
210 mObjectClasses
.insert( count(), objectclasses
);
211 kDebug() << "user: " << user
.getName() << " other objectclasses: " << objectclasses
.join(",");
215 if ( ( count() & 7 ) == 7 ) {
216 mProg
->setValue( mProg
->value() + mAdv
);
217 if ( mProg
->value() == 0 ) mAdv
= 1;
218 if ( mProg
->value() == mProg
->maximum()-1 ) mAdv
= -1;
222 bool KU_UserLDAP::reload()
224 kDebug() << "KU_UserLDAP::reload()";
225 mErrorString
= mErrorDetails
= QString();
226 mObjectClasses
.clear();
227 mProg
= new QProgressDialog( 0 );
228 mProg
->setLabel( new QLabel( i18n("Loading Users From LDAP") ) );
229 mProg
->setAutoClose( false );
230 mProg
->setAutoReset( false );
231 mProg
->setMaximum( 100 );
235 qApp
->processEvents();
236 KLDAP::LdapSearch search
;
239 SIGNAL( data( KLDAP::LdapSearch
*, const KLDAP::LdapObject
& ) ),
240 this, SLOT ( data ( KLDAP::LdapSearch
*, const KLDAP::LdapObject
&) ) );
242 SIGNAL( result( KLDAP::LdapSearch
* ) ),
243 this, SLOT ( result ( KLDAP::LdapSearch
* ) ) );
245 if (search
.search( mUrl
)) {
247 if ( mProg
->wasCanceled() ) search
.abandon();
249 kDebug() << "search failed";
251 mErrorString
= KLDAP::LdapConnection::errorString(search
.error());
252 mErrorDetails
= search
.errorString();
258 QString
KU_UserLDAP::getRDN(const KU_User
&user
) const
260 switch ( mCfg
->ldapuserrdn() ) {
261 case KU_PrefsBase::EnumLdapuserrdn::uid
:
262 return "uid=" + user
.getName();
263 case KU_PrefsBase::EnumLdapuserrdn::uidNumber
:
264 return "uidNumber=" + QString::number( user
.getUID() );
265 case KU_PrefsBase::EnumLdapuserrdn::cn
: {
266 QString cn
= mCfg
->ldapcnfullname() ? user
.getFullName() : user
.getName();
267 if ( cn
.isEmpty() ) cn
= user
.getName();
274 void KU_UserLDAP::createPassword( KU_User
&user
, const QString
&password
)
276 switch ( mCfg
->ldappasswordhash() ) {
277 case KU_PrefsBase::EnumLdappasswordhash::Clear
:
278 user
.setPwd( password
);
280 case KU_PrefsBase::EnumLdappasswordhash::CRYPT
:
281 user
.setPwd( "{CRYPT}" + encryptPass( password
, false ) );
283 case KU_PrefsBase::EnumLdappasswordhash::MD5
: {
284 QCryptographicHash
md5(QCryptographicHash::Md5
);
285 md5
.addData( password
.toUtf8() );
286 user
.setPwd( "{MD5}" + md5
.result().toBase64() );
289 case KU_PrefsBase::EnumLdappasswordhash::SMD5
: {
290 QCryptographicHash
md5(QCryptographicHash::Md5
);
291 QByteArray salt
= genSalt( 8 );
292 QByteArray pwd
= password
.toUtf8() + salt
;
295 user
.setPwd( "{SMD5}" + (md5
.result() + salt
).toBase64() );
298 case KU_PrefsBase::EnumLdappasswordhash::SHA
: {
299 QCryptographicHash
sha1(QCryptographicHash::Sha1
);
301 sha1
.addData( password
.toUtf8() );
302 user
.setPwd( "{SHA}" + sha1
.result().toBase64() );
305 case KU_PrefsBase::EnumLdappasswordhash::SSHA
: {
306 QCryptographicHash
sha1(QCryptographicHash::Sha1
);
308 QByteArray salt
= genSalt( 8 );
309 QByteArray pwd
= password
.toUtf8() + salt
;
312 user
.setPwd( "{SSHA}" + (sha1
.result() + salt
).toBase64() );
317 if ( caps
& Cap_Samba
) {
321 ntlmhash
= KNTLM::ntlmHash( password
);
322 unsigned char *hash
= (unsigned char*) ntlmhash
.data();
324 snprintf( (char*) &hex
, 33,
325 "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
326 hash
[0], hash
[1], hash
[2], hash
[3], hash
[4], hash
[5],
327 hash
[6], hash
[7], hash
[8], hash
[9], hash
[10], hash
[11],
328 hash
[12], hash
[13], hash
[14], hash
[15]);
330 user
.setNTPwd( QString::fromLatin1( (const char*) &hex
, 32 ) );
332 if ( mCfg
->lanmanhash() ) {
335 lmhash
= KNTLM::lmHash( password
);
336 unsigned char *hash
= (unsigned char*) lmhash
.data();
337 snprintf( (char*) &hex
, 33,
338 "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
339 hash
[0], hash
[1], hash
[2], hash
[3], hash
[4], hash
[5],
340 hash
[6], hash
[7], hash
[8], hash
[9], hash
[10], hash
[11],
341 hash
[12], hash
[13], hash
[14], hash
[15]);
343 user
.setLMPwd( QString::fromLatin1( (const char*) &hex
, 32 ) );
350 void KU_UserLDAP::createModStruct( const KU_User
&user
, int oldindex
, KLDAP::LdapOperation::ModOps
&ops
)
352 QString gecos
, cn
, pwd
, samflags
;
353 QList
<QByteArray
> vals
;
355 bool mod
= ( oldindex
!= -1 );
358 if ( user
.getDisabled() ) pwd
= "";
360 cn
= mCfg
->ldapcnfullname() ? user
.getFullName() : user
.getName();
361 if ( cn
.isEmpty() ) cn
= user
.getName();
363 gecos
= QString::fromLatin1("%1,%2,%3,%4")
364 .arg(user
.getFullName())
365 .arg(user
.getOffice1())
366 .arg(user
.getOffice2())
367 .arg(user
.getAddress());
370 samflags
+= user
.getDisabled() ? 'D' : ' ';
373 vals
.append( caps
& Cap_InetOrg
? "inetOrgPerson" : "account" );
374 if ( user
.getCaps() & KU_User::Cap_POSIX
) {
375 vals
.append( "posixAccount" );
377 if ( ( caps
& Cap_Shadow
) && ( user
.getCaps() & KU_User::Cap_POSIX
) ) {
378 vals
.append( "shadowAccount" );
380 if ( ( caps
& Cap_Samba
) && ( user
.getCaps() & KU_User::Cap_Samba
) ) {
381 vals
.append( "sambaSamAccount" );
384 if ( mod
&& mObjectClasses
.contains( oldindex
) ) {
385 QStringList ocs
= mObjectClasses
[ oldindex
];
386 kDebug() << user
.getName() << " has additional objectclasses: " << ocs
.join(",");
387 QStringList::iterator it
;
388 for ( it
= ocs
.begin(); it
!= ocs
.end(); ++it
) {
389 vals
.append( (*it
).toUtf8() );
392 ku_add2ops( ops
, "objectClass", vals
);
395 ku_add2ops( ops
, "cn", cn
.toUtf8() );
396 ku_add2ops( ops
, caps
& Cap_InetOrg
? "uid" : "userid", user
.getName().toUtf8() );
398 if ( ( user
.getCaps() & KU_User::Cap_POSIX
) || ( caps
& Cap_InetOrg
) ) {
399 ku_add2ops( ops
, "userpassword", pwd
.toUtf8(), true );
402 if ( user
.getCaps() & KU_User::Cap_POSIX
) {
403 ku_add2ops( ops
, "uidnumber", QString::number(user
.getUID()).toUtf8() );
404 ku_add2ops( ops
, "gidnumber", QString::number(user
.getGID()).toUtf8() );
405 ku_add2ops( ops
, "gecos", !mCfg
->ldapgecos() ? QByteArray() : QByteArray( gecos
.toLatin1() ) );
406 ku_add2ops( ops
, "homedirectory", user
.getHomeDir().toUtf8() );
407 ku_add2ops( ops
, "loginshell", user
.getShell().toUtf8() );
409 ku_add2ops( ops
, "uidnumber" );
410 ku_add2ops( ops
, "gidnumber" );
411 ku_add2ops( ops
, "gecos" );
412 ku_add2ops( ops
, "homedirectory" );
413 ku_add2ops( ops
, "loginshell" );
416 if ( caps
& Cap_InetOrg
) {
417 ku_add2ops( ops
, "sn", user
.getSurname().toUtf8() );
418 ku_add2ops( ops
, "mail", user
.getEmail().toUtf8() );
419 ku_add2ops( ops
, "displayName", user
.getFullName().toUtf8() );
420 ku_add2ops( ops
, "postaladdress", user
.getAddress().toUtf8() );
421 vals
.append( user
.getOffice1().toUtf8() );
422 vals
.append( user
.getOffice2().toUtf8() );
423 ku_add2ops( ops
, "telephoneNumber", vals
);
427 if ( caps
& Cap_Samba
) {
428 if ( user
.getCaps() & KU_User::Cap_Samba
) {
429 ku_add2ops( ops
, "sambadomainname", user
.getDomain().toUtf8() );
430 ku_add2ops( ops
, "sambauserworkstations", user
.getWorkstations().toUtf8() );
431 ku_add2ops( ops
, "sambahomepath", user
.getHomePath().toUtf8() );
432 ku_add2ops( ops
, "sambahomedrive", user
.getHomeDrive().toUtf8() );
433 ku_add2ops( ops
, "sambalogonscript", user
.getLoginScript().toUtf8() );
434 ku_add2ops( ops
, "sambaprofilepath", user
.getProfilePath().toUtf8() );
435 ku_add2ops( ops
, "sambalmpassword", user
.getLMPwd().toUtf8() );
436 ku_add2ops( ops
, "sambantpassword", user
.getNTPwd().toUtf8() );
437 ku_add2ops( ops
, "sambasid", user
.getSID().getSID().toUtf8() );
438 ku_add2ops( ops
, "sambaacctflags", samflags
.toUtf8() );
439 ku_add2ops( ops
, "sambaprimarygroupsid", user
.getPGSID().getSID().toUtf8() );
440 ku_add2ops( ops
, "sambapwdlastset", QString::number( user
.getLastChange() ).toUtf8() );
441 if ( user
.getExpire() != -1 )
442 vals
.append( QString::number( user
.getExpire() ).toUtf8() );
443 ku_add2ops( ops
, "sambakickofftime", vals
);
446 ku_add2ops( ops
, "sambadomainname" );
447 ku_add2ops( ops
, "sambauserworkstations" );
448 ku_add2ops( ops
, "sambahomepath" );
449 ku_add2ops( ops
, "sambahomedrive" );
450 ku_add2ops( ops
, "sambalogonscript" );
451 ku_add2ops( ops
, "sambaprofilepath" );
452 ku_add2ops( ops
, "sambalmpassword" );
453 ku_add2ops( ops
, "sambantpassword" );
454 ku_add2ops( ops
, "sambasid" );
455 ku_add2ops( ops
, "sambaacctflags" );
456 ku_add2ops( ops
, "sambaprimarygroupsid" );
457 ku_add2ops( ops
, "sambapwdlastset" );
458 ku_add2ops( ops
, "sambakickofftime" );
459 if ( schemaversion
> 0 ) {
460 ku_add2ops( ops
, "sambapasswordhistory" );
461 ku_add2ops( ops
, "sambalogonhours" );
466 if ( caps
& Cap_Shadow
) {
467 if ( user
.getCaps() & KU_User::Cap_POSIX
) {
468 ku_add2ops( ops
, "shadowlastchange", QString::number( timeToDays( user
.getLastChange() ) ).toUtf8() );
469 ku_add2ops( ops
, "shadowmin", QString::number( user
.getMin() ).toUtf8() );
470 ku_add2ops( ops
, "shadowmax", QString::number( user
.getMax() ).toUtf8() );
471 ku_add2ops( ops
, "shadowwarning", QString::number( user
.getWarn() ).toUtf8() );
472 ku_add2ops( ops
, "shadowinactive", QString::number( user
.getInactive() ).toUtf8() );
473 ku_add2ops( ops
, "shadowexpire", QString::number( timeToDays( user
.getExpire() ) ).toUtf8() );
474 ku_add2ops( ops
, "shadowflag", QString::number( user
.getFlag() ).toUtf8() );
476 ku_add2ops( ops
, "shadowlastchange" );
477 ku_add2ops( ops
, "shadowmin" );
478 ku_add2ops( ops
, "shadowmax" );
479 ku_add2ops( ops
, "shadowwarning" );
480 ku_add2ops( ops
, "shadowinactive" );
481 ku_add2ops( ops
, "shadowexpire" );
482 ku_add2ops( ops
, "shadowflag" );
487 bool KU_UserLDAP::dbcommit()
492 mErrorString
= mErrorDetails
= QString();
494 KLDAP::LdapConnection
conn( mUrl
);
496 if ( conn
.connect() != KLDAP_SUCCESS
) {
497 mErrorString
= conn
.connectionError();
501 KLDAP::LdapOperation
op( conn
);
503 if ( op
.bind_s() != KLDAP_SUCCESS
) {
504 mErrorString
= KLDAP::LdapConnection::errorString(conn
.ldapErrorCode());
505 mErrorDetails
= conn
.ldapErrorString();
509 KLDAP::LdapOperation::ModOps ops
;
511 mProg
= new QProgressDialog( 0 );
512 mProg
->setLabel( new QLabel(i18n("LDAP Operation") ) );
513 mProg
->setAutoClose( false );
514 mProg
->setAutoReset( false );
515 mProg
->setMaximum( mAdd
.count() + mDel
.count() + mMod
.count() );
518 for ( KU_Users::ModList::Iterator it
= mMod
.begin(); it
!= mMod
.end(); ++it
) {
519 QString oldrdn
= getRDN( at( it
.key() ) );
520 QString newrdn
= getRDN( it
.value() );
522 if ( oldrdn
!= newrdn
) {
523 int ret
= op
.rename_s( KLDAP::LdapDN( oldrdn
+ ',' + mUrl
.dn().toString() ),
525 mUrl
.dn().toString().toUtf8(),
528 if ( ret
!= KLDAP_SUCCESS
) {
529 mErrorString
= KLDAP::LdapConnection::errorString(conn
.ldapErrorCode());
530 mErrorDetails
= conn
.ldapErrorString();
537 createModStruct( it
.value(), it
.key(), ops
);
538 int ret
= op
.modify_s( KLDAP::LdapDN( getRDN( it
.value() ) + ',' + mUrl
.dn().toString() ), ops
);
539 if ( ret
!= KLDAP_SUCCESS
) {
540 mErrorString
= KLDAP::LdapConnection::errorString(conn
.ldapErrorCode());
541 mErrorDetails
= conn
.ldapErrorString();
545 mModSucc
.insert( it
.key(), it
.value() );
550 for ( KU_Users::AddList::Iterator it
= mAdd
.begin(); it
!= mAdd
.end(); ++it
) {
552 createModStruct( (*it
), -1, ops
);
553 kDebug() << "add name: " << (*it
).getName();
554 int ret
= op
.add_s( KLDAP::LdapDN( getRDN( (*it
) ) + ',' + mUrl
.dn().toString() ), ops
);
555 if ( ret
!= KLDAP_SUCCESS
) {
556 mErrorString
= KLDAP::LdapConnection::errorString(conn
.ldapErrorCode());
557 mErrorDetails
= conn
.ldapErrorString();
561 mAddSucc
.append( (*it
) );
566 for ( KU_Users::DelList::Iterator it
= mDel
.begin(); it
!= mDel
.end(); ++it
) {
567 kDebug() << "delete name: " << at((*it
)).getName();
568 int ret
= op
.del_s( KLDAP::LdapDN( getRDN( at((*it
)) ) + ',' + mUrl
.dn().toString() ) );
569 if ( ret
!= KLDAP_SUCCESS
) {
570 mErrorString
= KLDAP::LdapConnection::errorString(conn
.ldapErrorCode());
571 mErrorDetails
= conn
.ldapErrorString();
575 mDelSucc
.append( (*it
) );
583 #include "ku_userldap.moc"