00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <tqfile.h>
00026 #include <tqimage.h>
00027 #include <tqlabel.h>
00028 #include <tqpixmap.h>
00029 #include <tqtextstream.h>
00030 #include <tqurl.h>
00031
00032 #include <kabc/ldapurl.h>
00033 #include <kabc/ldif.h>
00034 #include <kapplication.h>
00035 #include <kconfig.h>
00036 #include <kdebug.h>
00037 #include <kdirwatch.h>
00038 #include <kmdcodec.h>
00039 #include <kprotocolinfo.h>
00040 #include <kstandarddirs.h>
00041 #include <kstaticdeleter.h>
00042
00043 #include "ldapclient.h"
00044
00045 using namespace KPIM;
00046
00047 KConfig *KPIM::LdapSearch::s_config = 0L;
00048 static KStaticDeleter<KConfig> configDeleter;
00049
00050 TQString LdapObject::toString() const
00051 {
00052 TQString result = TQString::fromLatin1( "\ndn: %1\n" ).arg( dn );
00053 for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) {
00054 TQString attr = it.key();
00055 for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) {
00056 result += TQString::fromUtf8( KABC::LDIF::assembleLine( attr, *it2, 76 ) ) + "\n";
00057 }
00058 }
00059
00060 return result;
00061 }
00062
00063 void LdapObject::clear()
00064 {
00065 dn = TQString();
00066 objectClass = TQString();
00067 attrs.clear();
00068 }
00069
00070 void LdapObject::assign( const LdapObject& that )
00071 {
00072 if ( &that != this ) {
00073 dn = that.dn;
00074 attrs = that.attrs;
00075 client = that.client;
00076 }
00077 }
00078
00079 LdapClient::LdapClient( int clientNumber, TQObject* parent, const char* name )
00080 : TQObject( parent, name ), mJob( 0 ), mActive( false ), mReportObjectClass( false )
00081 {
00082
00083 mClientNumber = clientNumber;
00084 mCompletionWeight = 50 - mClientNumber;
00085 }
00086
00087 LdapClient::~LdapClient()
00088 {
00089 cancelQuery();
00090
00091 }
00092
00093 void LdapClient::setAttrs( const TQStringList& attrs )
00094 {
00095 mAttrs = attrs;
00096 for ( TQStringList::Iterator it = mAttrs.begin(); it != mAttrs.end(); ++it )
00097 if( (*it).lower() == "objectclass" ){
00098 mReportObjectClass = true;
00099 return;
00100 }
00101 mAttrs << "objectClass";
00102 mReportObjectClass = false;
00103 }
00104
00105 void LdapClient::startQuery( const TQString& filter )
00106 {
00107 cancelQuery();
00108 KABC::LDAPUrl url;
00109
00110 url.setProtocol( ( mServer.security() == LdapServer::SSL ) ? "ldaps" : "ldap" );
00111 if ( mServer.auth() != LdapServer::Anonymous ) {
00112 url.setUser( mServer.user() );
00113 url.setPass( mServer.pwdBindDN() );
00114 }
00115 url.setHost( mServer.host() );
00116 url.setPort( mServer.port() );
00117 url.setExtension( "x-ver", TQString::number( mServer.version() ) );
00118 url.setDn( mServer.baseDN() );
00119 url.setDn( mServer.baseDN() );
00120 if ( mServer.security() == LdapServer::TLS ) url.setExtension( "x-tls","" );
00121 if ( mServer.auth() == LdapServer::SASL ) {
00122 url.setExtension( "x-sasl","" );
00123 if ( !mServer.bindDN().isEmpty() ) url.setExtension( "x-bindname", mServer.bindDN() );
00124 if ( !mServer.mech().isEmpty() ) url.setExtension( "x-mech", mServer.mech() );
00125 }
00126 if ( mServer.timeLimit() != 0 ) url.setExtension( "x-timelimit",
00127 TQString::number( mServer.timeLimit() ) );
00128 if ( mServer.sizeLimit() != 0 ) url.setExtension( "x-sizelimit",
00129 TQString::number( mServer.sizeLimit() ) );
00130
00131 url.setAttributes( mAttrs );
00132 url.setScope( mScope == "one" ? KABC::LDAPUrl::One : KABC::LDAPUrl::Sub );
00133 url.setFilter( "("+filter+")" );
00134
00135 kdDebug(5300) << "LdapClient: Doing query: " << url.prettyURL() << endl;
00136
00137 startParseLDIF();
00138 mActive = true;
00139 mJob = KIO::get( url, false, false );
00140 connect( mJob, TQT_SIGNAL( data( KIO::Job*, const TQByteArray& ) ),
00141 this, TQT_SLOT( slotData( KIO::Job*, const TQByteArray& ) ) );
00142 connect( mJob, TQT_SIGNAL( infoMessage( KIO::Job*, const TQString& ) ),
00143 this, TQT_SLOT( slotInfoMessage( KIO::Job*, const TQString& ) ) );
00144 connect( mJob, TQT_SIGNAL( result( KIO::Job* ) ),
00145 this, TQT_SLOT( slotDone() ) );
00146 }
00147
00148 void LdapClient::cancelQuery()
00149 {
00150 if ( mJob ) {
00151 mJob->kill();
00152 mJob = 0;
00153 }
00154
00155 mActive = false;
00156 }
00157
00158 void LdapClient::slotData( KIO::Job*, const TQByteArray& data )
00159 {
00160 parseLDIF( data );
00161 }
00162
00163 void LdapClient::slotInfoMessage( KIO::Job*, const TQString & )
00164 {
00165
00166 }
00167
00168 void LdapClient::slotDone()
00169 {
00170 endParseLDIF();
00171 mActive = false;
00172 #if 0
00173 for ( TQValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) {
00174 qDebug( (*it).toString().latin1() );
00175 }
00176 #endif
00177 int err = mJob->error();
00178 if ( err && err != KIO::ERR_USER_CANCELED ) {
00179 emit error( mJob->errorString() );
00180 }
00181 emit done();
00182 }
00183
00184 void LdapClient::startParseLDIF()
00185 {
00186 mCurrentObject.clear();
00187 mLdif.startParsing();
00188 }
00189
00190 void LdapClient::endParseLDIF()
00191 {
00192 }
00193
00194 void LdapClient::finishCurrentObject()
00195 {
00196 mCurrentObject.dn = mLdif.dn();
00197 const TQString sClass( mCurrentObject.objectClass.lower() );
00198 if( sClass == "groupofnames" || sClass == "kolabgroupofnames" ){
00199 LdapAttrMap::ConstIterator it = mCurrentObject.attrs.find("mail");
00200 if( it == mCurrentObject.attrs.end() ){
00201
00202
00203 TQString sMail;
00204 TQStringList lMail = TQStringList::split(",dc=", mCurrentObject.dn);
00205 const int n = lMail.count();
00206 if( n ){
00207 if( lMail.first().lower().startsWith("cn=") ){
00208 sMail = lMail.first().simplifyWhiteSpace().mid(3);
00209 if( 1 < n )
00210 sMail.append('@');
00211 for( int i=1; i<n; ++i){
00212 sMail.append( lMail[i] );
00213 if( i < n-1 )
00214 sMail.append('.');
00215 }
00216 mCurrentObject.attrs["mail"].append( sMail.utf8() );
00217 }
00218 }
00219 }
00220 }
00221 mCurrentObject.client = this;
00222 emit result( mCurrentObject );
00223 mCurrentObject.clear();
00224 }
00225
00226 void LdapClient::parseLDIF( const TQByteArray& data )
00227 {
00228
00229 if ( data.size() ) {
00230 mLdif.setLDIF( data );
00231 } else {
00232 mLdif.endLDIF();
00233 }
00234
00235 KABC::LDIF::ParseVal ret;
00236 TQString name;
00237 do {
00238 ret = mLdif.nextItem();
00239 switch ( ret ) {
00240 case KABC::LDIF::Item:
00241 {
00242 name = mLdif.attr();
00243
00244 TQByteArray value = mLdif.val().copy();
00245 bool bIsObjectClass = name.lower() == "objectclass";
00246 if( bIsObjectClass )
00247 mCurrentObject.objectClass = TQString::fromUtf8( value, value.size() );
00248 if( mReportObjectClass || !bIsObjectClass )
00249 mCurrentObject.attrs[ name ].append( value );
00250
00251 }
00252 break;
00253 case KABC::LDIF::EndEntry:
00254 finishCurrentObject();
00255 break;
00256 default:
00257 break;
00258 }
00259 } while ( ret != KABC::LDIF::MoreData );
00260 }
00261
00262 int LdapClient::clientNumber() const
00263 {
00264 return mClientNumber;
00265 }
00266
00267 int LdapClient::completionWeight() const
00268 {
00269 return mCompletionWeight;
00270 }
00271
00272 void LdapClient::setCompletionWeight( int weight )
00273 {
00274 mCompletionWeight = weight;
00275 }
00276
00277 void LdapSearch::readConfig( LdapServer &server, KConfig *config, int j, bool active )
00278 {
00279 TQString prefix;
00280 if ( active ) prefix = "Selected";
00281 TQString host = config->readEntry( prefix + TQString( "Host%1" ).arg( j ), "" ).stripWhiteSpace();
00282 if ( !host.isEmpty() )
00283 server.setHost( host );
00284
00285 int port = config->readNumEntry( prefix + TQString( "Port%1" ).arg( j ), 389 );
00286 server.setPort( port );
00287
00288 TQString base = config->readEntry( prefix + TQString( "Base%1" ).arg( j ), "" ).stripWhiteSpace();
00289 if ( !base.isEmpty() )
00290 server.setBaseDN( base );
00291
00292 TQString user = config->readEntry( prefix + TQString( "User%1" ).arg( j ) ).stripWhiteSpace();
00293 if ( !user.isEmpty() )
00294 server.setUser( user );
00295
00296 TQString bindDN = config->readEntry( prefix + TQString( "Bind%1" ).arg( j ) ).stripWhiteSpace();
00297 if ( !bindDN.isEmpty() )
00298 server.setBindDN( bindDN );
00299
00300 TQString pwdBindDN = config->readEntry( prefix + TQString( "PwdBind%1" ).arg( j ) );
00301 if ( !pwdBindDN.isEmpty() )
00302 server.setPwdBindDN( pwdBindDN );
00303
00304 server.setTimeLimit( config->readNumEntry( prefix + TQString( "TimeLimit%1" ).arg( j ) ) );
00305 server.setSizeLimit( config->readNumEntry( prefix + TQString( "SizeLimit%1" ).arg( j ) ) );
00306 server.setVersion( config->readNumEntry( prefix + TQString( "Version%1" ).arg( j ), 3 ) );
00307 server.setSecurity( config->readNumEntry( prefix + TQString( "Security%1" ).arg( j ) ) );
00308 server.setAuth( config->readNumEntry( prefix + TQString( "Auth%1" ).arg( j ) ) );
00309 server.setMech( config->readEntry( prefix + TQString( "Mech%1" ).arg( j ) ) );
00310 }
00311
00312 void LdapSearch::writeConfig( const LdapServer &server, KConfig *config, int j, bool active )
00313 {
00314 TQString prefix;
00315 if ( active ) prefix = "Selected";
00316 config->writeEntry( prefix + TQString( "Host%1" ).arg( j ), server.host() );
00317 config->writeEntry( prefix + TQString( "Port%1" ).arg( j ), server.port() );
00318 config->writeEntry( prefix + TQString( "Base%1" ).arg( j ), server.baseDN() );
00319 config->writeEntry( prefix + TQString( "User%1" ).arg( j ), server.user() );
00320 config->writeEntry( prefix + TQString( "Bind%1" ).arg( j ), server.bindDN() );
00321 config->writeEntry( prefix + TQString( "PwdBind%1" ).arg( j ), server.pwdBindDN() );
00322 config->writeEntry( prefix + TQString( "TimeLimit%1" ).arg( j ), server.timeLimit() );
00323 config->writeEntry( prefix + TQString( "SizeLimit%1" ).arg( j ), server.sizeLimit() );
00324 config->writeEntry( prefix + TQString( "Version%1" ).arg( j ), server.version() );
00325 config->writeEntry( prefix + TQString( "Security%1" ).arg( j ), server.security() );
00326 config->writeEntry( prefix + TQString( "Auth%1" ).arg( j ), server.auth() );
00327 config->writeEntry( prefix + TQString( "Mech%1" ).arg( j ), server.mech() );
00328 }
00329
00330 KConfig* LdapSearch::config()
00331 {
00332 if ( !s_config )
00333 configDeleter.setObject( s_config, new KConfig( "kabldaprc", false, false ) );
00334
00335 return s_config;
00336 }
00337
00338
00339 LdapSearch::LdapSearch()
00340 : mActiveClients( 0 ), mNoLDAPLookup( false )
00341 {
00342 if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) {
00343 mNoLDAPLookup = true;
00344 return;
00345 }
00346
00347 readConfig();
00348 connect(KDirWatch::self(), TQT_SIGNAL(dirty (const TQString&)),this,
00349 TQT_SLOT(slotFileChanged(const TQString&)));
00350 }
00351
00352 void LdapSearch::readWeighForClient( LdapClient *client, KConfig *config, int clientNumber )
00353 {
00354 const int completionWeight = config->readNumEntry( TQString( "SelectedCompletionWeight%1" ).arg( clientNumber ), -1 );
00355 if ( completionWeight != -1 )
00356 client->setCompletionWeight( completionWeight );
00357 }
00358
00359 void LdapSearch::updateCompletionWeights()
00360 {
00361 KConfig *config = KPIM::LdapSearch::config();
00362 config->setGroup( "LDAP" );
00363 for ( uint i = 0; i < mClients.size(); i++ ) {
00364 readWeighForClient( mClients[i], config, i );
00365 }
00366 }
00367
00368 void LdapSearch::readConfig()
00369 {
00370 cancelSearch();
00371 TQValueList< LdapClient* >::Iterator it;
00372 for ( it = mClients.begin(); it != mClients.end(); ++it )
00373 delete *it;
00374 mClients.clear();
00375
00376
00377 KConfig *config = KPIM::LdapSearch::config();
00378 config->setGroup( "LDAP" );
00379 int numHosts = config->readUnsignedNumEntry( "NumSelectedHosts");
00380 if ( !numHosts ) {
00381 mNoLDAPLookup = true;
00382 } else {
00383 for ( int j = 0; j < numHosts; j++ ) {
00384 LdapClient* ldapClient = new LdapClient( j, this );
00385 LdapServer server;
00386 readConfig( server, config, j, true );
00387 if ( !server.host().isEmpty() ) mNoLDAPLookup = false;
00388 ldapClient->setServer( server );
00389
00390 readWeighForClient( ldapClient, config, j );
00391
00392 TQStringList attrs;
00393
00394 attrs << "cn" << "mail" << "givenname" << "sn" << "objectClass";
00395 ldapClient->setAttrs( attrs );
00396
00397 connect( ldapClient, TQT_SIGNAL( result( const KPIM::LdapObject& ) ),
00398 this, TQT_SLOT( slotLDAPResult( const KPIM::LdapObject& ) ) );
00399 connect( ldapClient, TQT_SIGNAL( done() ),
00400 this, TQT_SLOT( slotLDAPDone() ) );
00401 connect( ldapClient, TQT_SIGNAL( error( const TQString& ) ),
00402 this, TQT_SLOT( slotLDAPError( const TQString& ) ) );
00403
00404 mClients.append( ldapClient );
00405 }
00406
00407 connect( &mDataTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotDataTimer() ) );
00408 }
00409 mConfigFile = locateLocal( "config", "kabldaprc" );
00410 KDirWatch::self()->addFile( mConfigFile );
00411 }
00412
00413 void LdapSearch::slotFileChanged( const TQString& file )
00414 {
00415 if ( file == mConfigFile )
00416 readConfig();
00417 }
00418
00419 void LdapSearch::startSearch( const TQString& txt )
00420 {
00421 if ( mNoLDAPLookup )
00422 return;
00423
00424 cancelSearch();
00425
00426 int pos = txt.find( '\"' );
00427 if( pos >= 0 )
00428 {
00429 ++pos;
00430 int pos2 = txt.find( '\"', pos );
00431 if( pos2 >= 0 )
00432 mSearchText = txt.mid( pos , pos2 - pos );
00433 else
00434 mSearchText = txt.mid( pos );
00435 } else
00436 mSearchText = txt;
00437
00438
00439
00440
00441
00442
00443
00444 TQString filter = TQString( "&(|(objectclass=person)(objectclass=groupOfNames)(mail=*))(|(cn=%1*)(mail=%2*)(mail=*@%3*)(givenName=%4*)(sn=%5*))" )
00445 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText );
00446
00447 TQValueList< LdapClient* >::Iterator it;
00448 for ( it = mClients.begin(); it != mClients.end(); ++it ) {
00449 (*it)->startQuery( filter );
00450 kdDebug(5300) << "LdapSearch::startSearch() " << filter << endl;
00451 ++mActiveClients;
00452 }
00453 }
00454
00455 void LdapSearch::cancelSearch()
00456 {
00457 TQValueList< LdapClient* >::Iterator it;
00458 for ( it = mClients.begin(); it != mClients.end(); ++it )
00459 (*it)->cancelQuery();
00460
00461 mActiveClients = 0;
00462 mResults.clear();
00463 }
00464
00465 void LdapSearch::slotLDAPResult( const KPIM::LdapObject& obj )
00466 {
00467 mResults.append( obj );
00468 if ( !mDataTimer.isActive() )
00469 mDataTimer.start( 500, true );
00470 }
00471
00472 void LdapSearch::slotLDAPError( const TQString& )
00473 {
00474 slotLDAPDone();
00475 }
00476
00477 void LdapSearch::slotLDAPDone()
00478 {
00479 if ( --mActiveClients > 0 )
00480 return;
00481
00482 finish();
00483 }
00484
00485 void LdapSearch::slotDataTimer()
00486 {
00487 TQStringList lst;
00488 LdapResultList reslist;
00489 makeSearchData( lst, reslist );
00490 if ( !lst.isEmpty() )
00491 emit searchData( lst );
00492 if ( !reslist.isEmpty() )
00493 emit searchData( reslist );
00494 }
00495
00496 void LdapSearch::finish()
00497 {
00498 mDataTimer.stop();
00499
00500 slotDataTimer();
00501 emit searchDone();
00502 }
00503
00504 void LdapSearch::makeSearchData( TQStringList& ret, LdapResultList& resList )
00505 {
00506 TQString search_text_upper = mSearchText.upper();
00507
00508 TQValueList< KPIM::LdapObject >::ConstIterator it1;
00509 for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) {
00510 TQString name, mail, givenname, sn;
00511 TQStringList mails;
00512 bool isDistributionList = false;
00513 bool wasCN = false;
00514 bool wasDC = false;
00515
00516
00517
00518 LdapAttrMap::ConstIterator it2;
00519 for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) {
00520 TQByteArray val = (*it2).first();
00521 int len = val.size();
00522 if( len > 0 && '\0' == val[len-1] )
00523 --len;
00524 const TQString tmp = TQString::fromUtf8( val, len );
00525
00526 if ( it2.key() == "cn" ) {
00527 name = tmp;
00528 if( mail.isEmpty() )
00529 mail = tmp;
00530 else{
00531 if( wasCN )
00532 mail.prepend( "." );
00533 else
00534 mail.prepend( "@" );
00535 mail.prepend( tmp );
00536 }
00537 wasCN = true;
00538 } else if ( it2.key() == "dc" ) {
00539 if( mail.isEmpty() )
00540 mail = tmp;
00541 else{
00542 if( wasDC )
00543 mail.append( "." );
00544 else
00545 mail.append( "@" );
00546 mail.append( tmp );
00547 }
00548 wasDC = true;
00549 } else if( it2.key() == "mail" ) {
00550 mail = tmp;
00551 LdapAttrValue::ConstIterator it3 = it2.data().begin();
00552 for ( ; it3 != it2.data().end(); ++it3 ) {
00553 mails.append( TQString::fromUtf8( (*it3).data(), (*it3).size() ) );
00554 }
00555 } else if( it2.key() == "givenName" )
00556 givenname = tmp;
00557 else if( it2.key() == "sn" )
00558 sn = tmp;
00559 else if( it2.key() == "objectClass" &&
00560 (tmp == "groupOfNames" || tmp == "kolabGroupOfNames") ) {
00561 isDistributionList = true;
00562 }
00563 }
00564
00565 if( mails.isEmpty()) {
00566 if ( !mail.isEmpty() ) mails.append( mail );
00567 if( isDistributionList ) {
00568
00569 ret.append( name );
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584 } else {
00585
00586 continue;
00587 }
00588 } else if ( name.isEmpty() ) {
00589
00590 ret.append( mail );
00591 } else {
00592
00593 ret.append( TQString( "%1 <%2>" ).arg( name ).arg( mail ) );
00594 }
00595
00596 LdapResult sr;
00597 sr.clientNumber = (*it1).client->clientNumber();
00598 sr.completionWeight = (*it1).client->completionWeight();
00599 sr.name = name;
00600 sr.email = mails;
00601 resList.append( sr );
00602 }
00603
00604 mResults.clear();
00605 }
00606
00607 bool LdapSearch::isAvailable() const
00608 {
00609 return !mNoLDAPLookup;
00610 }
00611
00612
00613 #include "ldapclient.moc"