00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <kdebug.h>
00022 #include <kglobal.h>
00023 #include <klineedit.h>
00024 #include <klocale.h>
00025 #include <kconfig.h>
00026 #include <kstringhandler.h>
00027
00028 #include <stdlib.h>
00029
00030 #include "resourceldap.h"
00031 #include "resourceldapconfig.h"
00032
00033 using namespace KABC;
00034
00035 void addModOp( LDAPMod ***pmods, const QString &attr, const QString &value );
00036
00037 ResourceLDAP::ResourceLDAP( const KConfig *config )
00038 : Resource( config ), mLdap( 0 )
00039 {
00040 if ( config ) {
00041 QMap<QString, QString> attrList;
00042 QStringList attributes = config->readListEntry( "LdapAttributes" );
00043 for ( uint pos = 0; pos < attributes.count(); pos += 2 )
00044 attrList.insert( attributes[ pos ], attributes[ pos + 1 ] );
00045
00046 init( config->readEntry( "LdapUser" ),
00047 KStringHandler::obscure( config->readEntry( "LdapPassword" ) ),
00048 config->readEntry( "LdapDn" ),
00049 config->readEntry( "LdapHost" ),
00050 config->readNumEntry( "LdapPort", 389 ),
00051 config->readEntry( "LdapFilter" ),
00052 config->readBoolEntry( "LdapAnonymous" ),
00053 attrList );
00054 } else {
00055 init( "", "", "", "", 389, "", true, QMap<QString, QString>() );
00056 }
00057 }
00058
00059 ResourceLDAP::ResourceLDAP( const QString &user, const QString &passwd,
00060 const QString &dn, const QString &host,
00061 int port, const QString &filter, bool anonymous,
00062 const QMap<QString, QString> &attributes )
00063 : Resource( 0 ), mLdap( 0 )
00064 {
00065 init( user, passwd, dn, host, port, filter, anonymous, attributes );
00066 }
00067
00068 void ResourceLDAP::init( const QString &user, const QString &passwd,
00069 const QString &dn, const QString &host,
00070 int port, const QString &filter, bool anonymous,
00071 const QMap<QString, QString> &attributes )
00072 {
00073 mUser = user;
00074 mPassword = passwd;
00075 mDn = dn;
00076 mHost = host;
00077 mPort = port;
00078 mFilter = filter;
00079 mAnonymous = anonymous;
00080
00087 if ( attributes.count() == 0 ) {
00088 mAttributes.insert( "commonName", "cn" );
00089 mAttributes.insert( "formattedName", "displayName" );
00090 mAttributes.insert( "familyName", "sn" );
00091 mAttributes.insert( "givenName", "givenName" );
00092 mAttributes.insert( "mail", "mail" );
00093 mAttributes.insert( "mailAlias", "" );
00094 mAttributes.insert( "phoneNumber", "telephoneNumber" );
00095 mAttributes.insert( "uid", "uid" );
00096 } else {
00097 mAttributes = attributes;
00098 }
00099 }
00100
00101 void ResourceLDAP::writeConfig( KConfig *config )
00102 {
00103 Resource::writeConfig( config );
00104
00105 config->writeEntry( "LdapUser", mUser );
00106 config->writeEntry( "LdapPassword", KStringHandler::obscure( mPassword ) );
00107 config->writeEntry( "LdapDn", mDn );
00108 config->writeEntry( "LdapHost", mHost );
00109 config->writeEntry( "LdapPort", mPort );
00110 config->writeEntry( "LdapFilter", mFilter );
00111 config->writeEntry( "LdapAnonymous", mAnonymous );
00112
00113 QStringList attributes;
00114 QMap<QString, QString>::Iterator it;
00115 for ( it = mAttributes.begin(); it != mAttributes.end(); ++it )
00116 attributes << it.key() << it.data();
00117
00118 config->writeEntry( "LdapAttributes", attributes );
00119 }
00120
00121 Ticket *ResourceLDAP::requestSaveTicket()
00122 {
00123 if ( !addressBook() ) {
00124 kdDebug(5700) << "no addressbook" << endl;
00125 return 0;
00126 }
00127
00128 return createTicket( this );
00129 }
00130
00131 void ResourceLDAP::releaseSaveTicket( Ticket *ticket )
00132 {
00133 delete ticket;
00134 }
00135
00136 bool ResourceLDAP::doOpen()
00137 {
00138 if ( mLdap )
00139 return false;
00140
00141 if ( !mPort )
00142 mPort = 389;
00143
00144 mLdap = ldap_init( mHost.local8Bit(), mPort );
00145 if ( !mLdap ) {
00146 addressBook()->error( i18n( "Unable to connect to server '%1' on port '%2'" ).arg( mHost ).arg( mPort ) );
00147 return false;
00148 }
00149
00150 if ( !mUser.isEmpty() && !mAnonymous ) {
00151 if ( ldap_simple_bind_s( mLdap, mUser.local8Bit(), mPassword.local8Bit() ) != LDAP_SUCCESS ) {
00152 addressBook()->error( i18n( "Unable to bind to server '%1'" ).arg( mHost ) );
00153 return false;
00154 }
00155
00156 kdDebug(5700) << "ResourceLDAP: bind to server successfully" << endl;
00157 } else {
00158 if ( ldap_simple_bind_s( mLdap, NULL, NULL ) != LDAP_SUCCESS ) {
00159 addressBook()->error( i18n( "Unable to bind anonymously to server '%1'" ).arg( mHost ) );
00160 return false;
00161 }
00162
00163 kdDebug( 5700 ) << "ResourceLDAP: bind anonymously to server successfully" << endl;
00164 }
00165
00166 int deref = LDAP_DEREF_ALWAYS;
00167 if ( ldap_set_option( mLdap, LDAP_OPT_DEREF, (void *) &deref ) != LDAP_OPT_SUCCESS ) {
00168 kdDebug(5700) << "ResourceLDAP: can't set 'deref' option" << endl;
00169 return false;
00170 }
00171
00172 if ( ldap_set_option( mLdap, LDAP_OPT_REFERRALS, LDAP_OPT_ON ) != LDAP_OPT_SUCCESS ) {
00173 kdDebug(5700) << "ResourceLDAP: can't set 'referrals' option" << endl;
00174 return false;
00175 }
00176
00177 return true;
00178 }
00179
00180 void ResourceLDAP::doClose()
00181 {
00182 if ( ldap_unbind_s( mLdap ) != LDAP_SUCCESS ) {
00183 kdDebug(5700) << "ResourceLDAP: can't unbind from server" << endl;
00184 return;
00185 }
00186
00187 mLdap = 0;
00188 }
00189
00190 bool ResourceLDAP::load()
00191 {
00192 LDAPMessage *res;
00193 LDAPMessage *msg;
00194 BerElement *track;
00195 char *names;
00196 char **values;
00197
00198 char **LdapSearchAttr = new char*[ mAttributes.count() + 1 ];
00199
00200 QMap<QString, QString>::Iterator it;
00201 int i = 0;
00202 for ( it = mAttributes.begin(); it != mAttributes.end(); ++it ) {
00203 if ( !it.data().isEmpty() ) {
00204 unsigned int len = it.data().utf8().length();
00205 LdapSearchAttr[ i ] = new char[ len+1 ];
00206 memcpy( LdapSearchAttr[ i ], it.data().utf8(), len );
00207 LdapSearchAttr[ i ][ len ] = 0;
00208 ++i;
00209 }
00210 }
00211 LdapSearchAttr[ i ] = 0;
00212
00213 QString filter = mFilter;
00214 if ( filter.isEmpty() )
00215 filter = "cn=*";
00216
00217 int result;
00218 if ( ( result = ldap_search_s( mLdap, mDn.local8Bit(), LDAP_SCOPE_SUBTREE, QString( "(%1)" ).arg( filter ).local8Bit(),
00219 LdapSearchAttr, 0, &res ) != LDAP_SUCCESS ) ) {
00220 addressBook()->error( i18n( "Unable to search on server '%1': %2" )
00221 .arg( mHost )
00222 .arg( ldap_err2string( result ) ) );
00223
00224 for ( i = 0; LdapSearchAttr[ i ]; ++i )
00225 delete [] LdapSearchAttr[ i ];
00226 delete [] LdapSearchAttr;
00227
00228 return false;
00229 }
00230
00231 for ( msg = ldap_first_entry( mLdap, res ); msg; msg = ldap_next_entry( mLdap, msg ) ) {
00232 Addressee addr;
00233 addr.setResource( this );
00234 for ( names = ldap_first_attribute( mLdap, msg, &track ); names; names = ldap_next_attribute( mLdap, msg, track ) ) {
00235 values = ldap_get_values( mLdap, msg, names );
00236 for ( int i = 0; i < ldap_count_values( values ); ++i ) {
00237 QString name = QString::fromUtf8( names ).lower();
00238 QString value = QString::fromUtf8( values[ i ] );
00239
00240 if ( name == mAttributes[ "commonName" ].lower() ) {
00241 if ( !addr.formattedName().isEmpty() ) {
00242 QString fn = addr.formattedName();
00243 addr.setNameFromString( value );
00244 addr.setFormattedName( fn );
00245 } else
00246 addr.setNameFromString( value );
00247 } else if ( name == mAttributes[ "formattedName" ].lower() ) {
00248 addr.setFormattedName( value );
00249 } else if ( name == mAttributes[ "givenName" ].lower() ) {
00250 addr.setGivenName( value );
00251 } else if ( name == mAttributes[ "mail" ].lower() ) {
00252 addr.insertEmail( value, true );
00253 } else if ( name == mAttributes[ "mailAlias" ].lower() ) {
00254 addr.insertEmail( value, false );
00255 } else if ( name == mAttributes[ "phoneNumber" ].lower() ) {
00256 PhoneNumber phone;
00257 phone.setNumber( value );
00258 addr.insertPhoneNumber( phone );
00259 break;
00260 } else if ( name == mAttributes[ "familyName" ].lower() ) {
00261 addr.setFamilyName( value );
00262 } else if ( name == mAttributes[ "uid" ].lower() ) {
00263 addr.setUid( value );
00264 }
00265 }
00266 ldap_value_free( values );
00267 }
00268 ber_free( track, 0 );
00269
00270 addressBook()->insertAddressee( addr );
00271 }
00272
00273 ldap_msgfree( res );
00274
00275 for ( i = 0; LdapSearchAttr[ i ]; ++i )
00276 delete [] LdapSearchAttr[ i ];
00277 delete [] LdapSearchAttr;
00278
00279 return true;
00280 }
00281
00282 bool ResourceLDAP::asyncLoad()
00283 {
00284 bool ok = load();
00285 if ( !ok )
00286 emit loadingError( this, i18n( "Loading resource '%1' failed!" )
00287 .arg( resourceName() ) );
00288 else
00289 emit loadingFinished( this );
00290
00291 return ok;
00292 }
00293
00294 bool ResourceLDAP::save( Ticket* )
00295 {
00296 AddressBook::Iterator it;
00297 for ( it = addressBook()->begin(); it != addressBook()->end(); ++it ) {
00298 if ( (*it).resource() == this && (*it).changed() ) {
00299 LDAPMod **mods = NULL;
00300
00301 addModOp( &mods, "objectClass", "organizationalPerson" );
00302 addModOp( &mods, "objectClass", "person" );
00303 addModOp( &mods, "objectClass", "Top" );
00304 addModOp( &mods, mAttributes[ "commonName" ].utf8(), (*it).assembledName() );
00305 addModOp( &mods, mAttributes[ "formattedName" ].utf8(), (*it).formattedName() );
00306 addModOp( &mods, mAttributes[ "givenName" ].utf8(), (*it).givenName() );
00307 addModOp( &mods, mAttributes[ "familyName" ].utf8(), (*it).familyName() );
00308 addModOp( &mods, mAttributes[ "uid" ].utf8(), (*it).uid() );
00309
00310 QStringList emails = (*it).emails();
00311 QStringList::ConstIterator mailIt;
00312 bool first = true;
00313 for ( mailIt = emails.begin(); mailIt != emails.end(); ++mailIt ) {
00314 if ( first ) {
00315 addModOp( &mods, mAttributes[ "mail" ].utf8(), (*mailIt) );
00316 first = false;
00317 } else
00318 addModOp( &mods, mAttributes[ "mailAlias" ].utf8(), (*mailIt) );
00319 }
00320
00321 PhoneNumber number = (*it).phoneNumber( PhoneNumber::Home );
00322 addModOp( &mods, mAttributes[ "phoneNumber" ].utf8(), number.number() );
00323
00324 QString dn = "cn=" + (*it).assembledName() + "," + mDn;
00325
00326 int retval;
00327 if ( (retval = ldap_add_s( mLdap, dn.local8Bit(), mods )) != LDAP_SUCCESS )
00328 addressBook()->error( i18n( "Unable to modify '%1' on server '%2'" ).arg( (*it).uid() ).arg( mHost ) );
00329
00330 ldap_mods_free( mods, 1 );
00331
00332
00333 (*it).setChanged( false );
00334 }
00335 }
00336
00337 return true;
00338 }
00339
00340 bool ResourceLDAP::asyncSave( Ticket *ticket )
00341 {
00342 bool ok = save( ticket );
00343 if ( !ok )
00344 emit savingError( this, i18n( "Saving resource '%1' failed!" )
00345 .arg( resourceName() ) );
00346 else
00347 emit savingFinished( this );
00348
00349 return ok;
00350 }
00351
00352 void ResourceLDAP::removeAddressee( const Addressee &addr )
00353 {
00354 LDAPMessage *res;
00355 LDAPMessage *msg;
00356
00357 QString filter = QString( "(&(uid=%1)(%2))" ).arg( addr.uid() ).arg( mFilter );
00358
00359 kdDebug(5700) << "ldap:removeAddressee" << filter << endl;
00360
00361 ldap_search_s( mLdap, mDn.local8Bit(), LDAP_SCOPE_SUBTREE, filter.local8Bit(),
00362 0, 0, &res );
00363
00364 bool ok = true;
00365 for ( msg = ldap_first_entry( mLdap, res ); msg; msg = ldap_next_entry( mLdap, msg ) ) {
00366 char *dn = ldap_get_dn( mLdap, msg );
00367 kdDebug(5700) << "found " << dn << endl;
00368 if ( ldap_delete_s( mLdap, dn ) != LDAP_SUCCESS ) {
00369 addressBook()->error( i18n( "Unable to delete '%1' on server '%2'" ).arg( dn ).arg( mHost ) );
00370 ok = false;
00371 }
00372
00373 ldap_memfree( dn );
00374 }
00375
00376 ldap_msgfree( res );
00377
00378 if ( ok )
00379 mAddrMap.erase( addr.uid() );
00380 }
00381
00382 void ResourceLDAP::setUser( const QString &user )
00383 {
00384 mUser = user;
00385 }
00386
00387 QString ResourceLDAP::user() const
00388 {
00389 return mUser;
00390 }
00391
00392 void ResourceLDAP::setPassword( const QString &password )
00393 {
00394 mPassword = password;
00395 }
00396
00397 QString ResourceLDAP::password() const
00398 {
00399 return mPassword;
00400 }
00401
00402 void ResourceLDAP::setDn( const QString &dn )
00403 {
00404 mDn = dn;
00405 }
00406
00407 QString ResourceLDAP::dn() const
00408 {
00409 return mDn;
00410 }
00411
00412 void ResourceLDAP::setHost( const QString &host )
00413 {
00414 mHost = host;
00415 }
00416
00417 QString ResourceLDAP::host() const
00418 {
00419 return mHost;
00420 }
00421
00422 void ResourceLDAP::setPort( int port )
00423 {
00424 mPort = port;
00425 }
00426
00427 int ResourceLDAP::port() const
00428 {
00429 return mPort;
00430 }
00431
00432 void ResourceLDAP::setFilter( const QString &filter )
00433 {
00434 mFilter = filter;
00435 }
00436
00437 QString ResourceLDAP::filter() const
00438 {
00439 return mFilter;
00440 }
00441
00442 void ResourceLDAP::setIsAnonymous( bool value )
00443 {
00444 mAnonymous = value;
00445 }
00446
00447 bool ResourceLDAP::isAnonymous() const
00448 {
00449 return mAnonymous;
00450 }
00451
00452 void ResourceLDAP::setAttributes( const QMap<QString, QString> &attributes )
00453 {
00454 mAttributes = attributes;
00455 }
00456
00457 QMap<QString, QString> ResourceLDAP::attributes() const
00458 {
00459 return mAttributes;
00460 }
00461
00462 void addModOp( LDAPMod ***pmods, const QString &attr, const QString &value )
00463 {
00464 if ( value.isNull() )
00465 return;
00466
00467 LDAPMod **mods;
00468
00469 mods = *pmods;
00470
00471 uint i = 0;
00472 if ( mods != 0 )
00473 for ( ; mods[ i ] != 0; ++i );
00474
00475 if (( mods = (LDAPMod **)realloc( mods, (i + 2) * sizeof( LDAPMod * ))) == 0 ) {
00476 kdError() << "ResourceLDAP: realloc" << endl;
00477 return;
00478 }
00479
00480 *pmods = mods;
00481 mods[ i + 1 ] = 0;
00482
00483 mods[ i ] = new LDAPMod;
00484
00485 mods[ i ]->mod_op = 0;
00486 mods[ i ]->mod_type = strdup( attr.utf8() );
00487 mods[ i ]->mod_values = new char*[ 2 ];
00488 mods[ i ]->mod_values[ 0 ] = strdup( value.utf8() );
00489 mods[ i ]->mod_values[ 1 ] = 0;
00490 }
00491