libkdepim Library API Documentation

ldapclient.cpp

00001 /* kldapclient.cpp - LDAP access 00002 * Copyright (C) 2002 Klarälvdalens Datakonsult AB 00003 * 00004 * Author: Steffen Hansen <hansen@kde.org> 00005 * 00006 * Ported to KABC by Daniel Molkentin <molkentin@kde.org> 00007 * 00008 * This file is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * This file is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; if not, write to the Free Software 00020 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 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"; // via objectClass we detect distribution lists 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 #ifndef NDEBUG // don't create the QString 00176 // QString str( data ); 00177 // kdDebug(5700) << "LdapClient: Got \"" << str << "\"\n"; 00178 #endif 00179 parseLDIF( data ); 00180 } 00181 00182 void LdapClient::slotInfoMessage( KIO::Job*, const QString & ) 00183 { 00184 //qDebug("Job said \"%s\"", info.latin1()); 00185 } 00186 00187 void LdapClient::slotDone() 00188 { 00189 endParseLDIF(); 00190 mActive = false; 00191 #if 0 00192 for ( QValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) { 00193 qDebug( (*it).toString().latin1() ); 00194 } 00195 #endif 00196 int err = mJob->error(); 00197 if ( err && err != KIO::ERR_USER_CANCELED ) { 00198 emit error( KIO::buildErrorString( err, QString("%1:%2").arg( mHost ).arg( mPort ) ) ); 00199 } 00200 emit done(); 00201 } 00202 00203 void LdapClient::startParseLDIF() 00204 { 00205 mCurrentObject.clear(); 00206 mLastAttrName = 0; 00207 mLastAttrValue = 0; 00208 mIsBase64 = false; 00209 d->ldif.startParsing(); 00210 } 00211 00212 void LdapClient::endParseLDIF() 00213 { 00214 } 00215 00216 void LdapClient::finishCurrentObject() 00217 { 00218 mCurrentObject.dn = d->ldif.dn(); 00219 const QString sClass( mCurrentObject.objectClass.lower() ); 00220 if( sClass == "groupofnames" || sClass == "kolabgroupofnames" ){ 00221 LdapAttrMap::ConstIterator it = mCurrentObject.attrs.find("mail"); 00222 if( it == mCurrentObject.attrs.end() ){ 00223 // No explicit mail address found so far? 00224 // Fine, then we use the address stored in the DN. 00225 QString sMail; 00226 QStringList lMail = QStringList::split(",dc=", mCurrentObject.dn); 00227 const int n = lMail.count(); 00228 if( n ){ 00229 if( lMail.first().lower().startsWith("cn=") ){ 00230 sMail = lMail.first().simplifyWhiteSpace().mid(3); 00231 if( 1 < n ) 00232 sMail.append('@'); 00233 for( int i=1; i<n; ++i){ 00234 sMail.append( lMail[i] ); 00235 if( i < n-1 ) 00236 sMail.append('.'); 00237 } 00238 mCurrentObject.attrs["mail"].append( sMail.utf8() ); 00239 } 00240 } 00241 } 00242 } 00243 mCurrentObject.client = this; 00244 emit result( mCurrentObject ); 00245 mCurrentObject.clear(); 00246 } 00247 00248 void LdapClient::parseLDIF( const QByteArray& data ) 00249 { 00250 //kdDebug(5300) << "LdapClient::parseLDIF( " << QCString(data) << " )" << endl; 00251 if ( data.size() ) { 00252 d->ldif.setLDIF( data ); 00253 } else { 00254 d->ldif.endLDIF(); 00255 } 00256 00257 LDIF::ParseVal ret; 00258 QString name; 00259 QByteArray value; 00260 do { 00261 ret = d->ldif.nextItem(); 00262 switch ( ret ) { 00263 case LDIF::Item: 00264 { 00265 name = d->ldif.attr(); 00266 value = d->ldif.val(); 00267 bool bFoundOC = name.lower() == "objectclass"; 00268 if( bFoundOC ) 00269 mCurrentObject.objectClass = QString::fromUtf8( value, value.size() ); 00270 if( mReportObjectClass || !bFoundOC ) 00271 mCurrentObject.attrs[ name ].append( value ); 00272 //kdDebug(5300) << "LdapClient::parseLDIF()" << name << " / " << value << endl; 00273 } 00274 break; 00275 case LDIF::EndEntry: 00276 finishCurrentObject(); 00277 break; 00278 default: 00279 break; 00280 } 00281 } while ( ret != LDIF::MoreData ); 00282 } 00283 00284 QString LdapClient::bindDN() const 00285 { 00286 return d->bindDN; 00287 } 00288 00289 QString LdapClient::pwdBindDN() const 00290 { 00291 return d->pwdBindDN; 00292 } 00293 00294 int LdapClient::clientNumber() const 00295 { 00296 return d->clientNumber; 00297 } 00298 00299 int LdapClient::completionWeight() const 00300 { 00301 return d->completionWeight; 00302 } 00303 00304 void KPIM::LdapClient::setCompletionWeight( int weight ) 00305 { 00306 d->completionWeight = weight; 00307 } 00308 00309 LdapSearch::LdapSearch() 00310 : mActiveClients( 0 ), mNoLDAPLookup( false ) 00311 { 00312 if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) { 00313 mNoLDAPLookup = true; 00314 return; 00315 } 00316 00317 readConfig(); 00318 connect(KDirWatch::self(), SIGNAL(dirty (const QString&)),this, 00319 SLOT(slotFileChanged(const QString&))); 00320 } 00321 00322 void LdapSearch::readConfig() 00323 { 00324 //kdDebug(5300) << "LdapClient::readConfig()" << endl; 00325 cancelSearch(); 00326 QValueList< LdapClient* >::Iterator it; 00327 for ( it = mClients.begin(); it != mClients.end(); ++it ) 00328 delete *it; 00329 mClients.clear(); 00330 00331 // stolen from KAddressBook 00332 KConfig config( "kabldaprc", true ); 00333 config.setGroup( "LDAP" ); 00334 int numHosts = config.readUnsignedNumEntry( "NumSelectedHosts"); 00335 if ( !numHosts ) { 00336 mNoLDAPLookup = true; 00337 } else { 00338 for ( int j = 0; j < numHosts; j++ ) { 00339 LdapClient* ldapClient = new LdapClient( j, this ); 00340 00341 QString host = config.readEntry( QString( "SelectedHost%1" ).arg( j ), "" ).stripWhiteSpace(); 00342 if ( !host.isEmpty() ){ 00343 ldapClient->setHost( host ); 00344 mNoLDAPLookup = false; 00345 } 00346 00347 QString port = QString::number( config.readUnsignedNumEntry( QString( "SelectedPort%1" ).arg( j ) ) ); 00348 if ( !port.isEmpty() ) 00349 ldapClient->setPort( port ); 00350 00351 QString base = config.readEntry( QString( "SelectedBase%1" ).arg( j ), "" ).stripWhiteSpace(); 00352 if ( !base.isEmpty() ) 00353 ldapClient->setBase( base ); 00354 00355 QString bindDN = config.readEntry( QString( "SelectedBind%1" ).arg( j ) ).stripWhiteSpace(); 00356 if ( !bindDN.isEmpty() ) 00357 ldapClient->setBindDN( bindDN ); 00358 00359 QString pwdBindDN = config.readEntry( QString( "SelectedPwdBind%1" ).arg( j ) ).stripWhiteSpace(); 00360 if ( !pwdBindDN.isEmpty() ) 00361 ldapClient->setPwdBindDN( pwdBindDN ); 00362 00363 int completionWeight = config.readNumEntry( QString( "SelectedCompletionWeight%1" ).arg( j ), -1 ); 00364 if ( completionWeight != -1 ) 00365 ldapClient->setCompletionWeight( completionWeight ); 00366 00367 QStringList attrs; 00368 // note: we need "objectClass" to detect distribution lists 00369 attrs << "cn" << "mail" << "givenname" << "sn" << "objectClass"; 00370 ldapClient->setAttrs( attrs ); 00371 00372 connect( ldapClient, SIGNAL( result( const KPIM::LdapObject& ) ), 00373 this, SLOT( slotLDAPResult( const KPIM::LdapObject& ) ) ); 00374 connect( ldapClient, SIGNAL( done() ), 00375 this, SLOT( slotLDAPDone() ) ); 00376 connect( ldapClient, SIGNAL( error( const QString& ) ), 00377 this, SLOT( slotLDAPError( const QString& ) ) ); 00378 00379 mClients.append( ldapClient ); 00380 } 00381 00382 connect( &mDataTimer, SIGNAL( timeout() ), SLOT( slotDataTimer() ) ); 00383 } 00384 mConfigFile = locateLocal( "config", "kabldaprc" ); 00385 KDirWatch::self()->addFile( mConfigFile ); 00386 } 00387 00388 void LdapSearch::slotFileChanged( const QString& file ) 00389 { 00390 //kdDebug(5300) << "LdapClient::slotFileChanged( " << file << " )" << endl; 00391 if ( file == mConfigFile ) 00392 readConfig(); 00393 } 00394 00395 void LdapSearch::startSearch( const QString& txt ) 00396 { 00397 if ( mNoLDAPLookup ) 00398 return; 00399 00400 cancelSearch(); 00401 00402 int pos = txt.find( '\"' ); 00403 if( pos >= 0 ) 00404 { 00405 ++pos; 00406 int pos2 = txt.find( '\"', pos ); 00407 if( pos2 >= 0 ) 00408 mSearchText = txt.mid( pos , pos2 - pos ); 00409 else 00410 mSearchText = txt.mid( pos ); 00411 } else 00412 mSearchText = txt; 00413 00414 QString filter = QString( "|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" ) 00415 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText ); 00416 00417 QValueList< LdapClient* >::Iterator it; 00418 for ( it = mClients.begin(); it != mClients.end(); ++it ) { 00419 (*it)->startQuery( filter ); 00420 kdDebug(5300) << "LdapSearch::startSearch() " << filter << endl; 00421 ++mActiveClients; 00422 } 00423 } 00424 00425 void LdapSearch::cancelSearch() 00426 { 00427 QValueList< LdapClient* >::Iterator it; 00428 for ( it = mClients.begin(); it != mClients.end(); ++it ) 00429 (*it)->cancelQuery(); 00430 00431 mActiveClients = 0; 00432 mResults.clear(); 00433 } 00434 00435 void LdapSearch::slotLDAPResult( const KPIM::LdapObject& obj ) 00436 { 00437 mResults.append( obj ); 00438 if ( !mDataTimer.isActive() ) 00439 mDataTimer.start( 500, true ); 00440 } 00441 00442 void LdapSearch::slotLDAPError( const QString& ) 00443 { 00444 slotLDAPDone(); 00445 } 00446 00447 void LdapSearch::slotLDAPDone() 00448 { 00449 if ( --mActiveClients > 0 ) 00450 return; 00451 00452 finish(); 00453 } 00454 00455 void LdapSearch::slotDataTimer() 00456 { 00457 QStringList lst; 00458 LdapResultList reslist; 00459 makeSearchData( lst, reslist ); 00460 if ( !lst.isEmpty() ) 00461 emit searchData( lst ); 00462 if ( !reslist.isEmpty() ) 00463 emit searchData( reslist ); 00464 } 00465 00466 void LdapSearch::finish() 00467 { 00468 mDataTimer.stop(); 00469 00470 slotDataTimer(); // emit final bunch of data 00471 emit searchDone(); 00472 } 00473 00474 void LdapSearch::makeSearchData( QStringList& ret, LdapResultList& resList ) 00475 { 00476 QString search_text_upper = mSearchText.upper(); 00477 00478 QValueList< KPIM::LdapObject >::ConstIterator it1; 00479 for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) { 00480 QString name, mail, givenname, sn; 00481 QStringList mails; 00482 bool isDistributionList = false; 00483 bool wasCN = false; 00484 bool wasDC = false; 00485 00486 kdDebug(5300) << "\n\nLdapSearch::makeSearchData()\n\n" << endl; 00487 00488 LdapAttrMap::ConstIterator it2; 00489 for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) { 00490 QByteArray val = (*it2).first(); 00491 int len = val.size(); 00492 if( len > 0 && '\0' == val[len-1] ) 00493 --len; 00494 const QString tmp = QString::fromUtf8( val, len ); 00495 kdDebug(5300) << " key: \"" << it2.key() << "\" value: \"" << tmp << "\"" << endl; 00496 if ( it2.key() == "cn" ) { 00497 name = tmp; 00498 if( mail.isEmpty() ) 00499 mail = tmp; 00500 else{ 00501 if( wasCN ) 00502 mail.prepend( "." ); 00503 else 00504 mail.prepend( "@" ); 00505 mail.prepend( tmp ); 00506 } 00507 wasCN = true; 00508 } else if ( it2.key() == "dc" ) { 00509 if( mail.isEmpty() ) 00510 mail = tmp; 00511 else{ 00512 if( wasDC ) 00513 mail.append( "." ); 00514 else 00515 mail.append( "@" ); 00516 mail.append( tmp ); 00517 } 00518 wasDC = true; 00519 } else if( it2.key() == "mail" ) { 00520 mail = tmp; 00521 LdapAttrValue::ConstIterator it3 = it2.data().begin(); 00522 for ( ; it3 != it2.data().end(); ++it3 ) { 00523 mails.append( QString::fromUtf8( (*it3).data(), (*it3).size() ) ); 00524 } 00525 } else if( it2.key() == "givenName" ) 00526 givenname = tmp; 00527 else if( it2.key() == "sn" ) 00528 sn = tmp; 00529 else if( it2.key() == "objectClass" && 00530 ( tmp == "groupOfNames" || tmp == "kolabGroupOfNames" ) ) { 00531 isDistributionList = true; 00532 } 00533 } 00534 00535 if( mails.isEmpty()) { 00536 if ( !mail.isEmpty() ) mails.append( mail ); 00537 if( isDistributionList ) { 00538 kdDebug(5300) << "\n\nLdapSearch::makeSearchData() found a list: " << name << "\n\n" << endl; 00539 ret.append( name ); 00540 // following lines commented out for bugfixing kolab issue #177: 00541 // 00542 // Unlike we thought previously we may NOT append the server name here. 00543 // 00544 // The right server is found by the SMTP server instead: Kolab users 00545 // must use the correct SMTP server, by definition. 00546 // 00547 //mail = (*it1).client->base().simplifyWhiteSpace(); 00548 //mail.replace( ",dc=", ".", false ); 00549 //if( mail.startsWith("dc=", false) ) 00550 // mail.remove(0, 3); 00551 //mail.prepend( '@' ); 00552 //mail.prepend( name ); 00553 //mail = name; 00554 } else { 00555 kdDebug(5300) << "LdapSearch::makeSearchData() found BAD ENTRY: \"" << name << "\"" << endl; 00556 continue; // nothing, bad entry 00557 } 00558 } else if ( name.isEmpty() ) { 00559 kdDebug(5300) << "LdapSearch::makeSearchData() mail: \"" << mail << "\"" << endl; 00560 ret.append( mail ); 00561 } else { 00562 kdDebug(5300) << "LdapSearch::makeSearchData() name: \"" << name << "\" mail: \"" << mail << "\"" << endl; 00563 ret.append( QString( "%1 <%2>" ).arg( name ).arg( mail ) ); 00564 } 00565 00566 LdapResult sr; 00567 sr.clientNumber = (*it1).client->clientNumber(); 00568 sr.completionWeight = (*it1).client->completionWeight(); 00569 sr.name = name; 00570 sr.email = mails; 00571 resList.append( sr ); 00572 } 00573 00574 mResults.clear(); 00575 } 00576 00577 bool LdapSearch::isAvailable() const 00578 { 00579 return !mNoLDAPLookup; 00580 } 00581 00582 #include "ldapclient.moc"
KDE Logo
This file is part of the documentation for libkdepim Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Oct 1 15:18:55 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003