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