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 if( mCurrentObject.objectClass.lower() == "groupofnames" ){ 00220 LdapAttrMap::ConstIterator it = mCurrentObject.attrs.find("mail"); 00221 if( it == mCurrentObject.attrs.end() ){ 00222 // No explicit mail address found so far? 00223 // Fine, then we use the address stored in the DN. 00224 QString sMail; 00225 QStringList lMail = QStringList::split(",dc=", mCurrentObject.dn); 00226 const int n = lMail.count(); 00227 if( n ){ 00228 if( lMail.first().lower().startsWith("cn=") ){ 00229 sMail = lMail.first().simplifyWhiteSpace().mid(3); 00230 if( 1 < n ) 00231 sMail.append('@'); 00232 for( int i=1; i<n; ++i){ 00233 sMail.append( lMail[i] ); 00234 if( i < n-1 ) 00235 sMail.append('.'); 00236 } 00237 mCurrentObject.attrs["mail"].append( sMail.utf8() ); 00238 } 00239 } 00240 } 00241 } 00242 mCurrentObject.client = this; 00243 emit result( mCurrentObject ); 00244 mCurrentObject.clear(); 00245 } 00246 00247 void LdapClient::parseLDIF( const QByteArray& data ) 00248 { 00249 //kdDebug(5300) << "LdapClient::parseLDIF( " << QCString(data) << " )" << endl; 00250 if ( data.size() ) { 00251 d->ldif.setLDIF( data ); 00252 } else { 00253 d->ldif.endLDIF(); 00254 } 00255 00256 LDIF::ParseVal ret; 00257 QString name; 00258 QByteArray value; 00259 do { 00260 ret = d->ldif.nextItem(); 00261 switch ( ret ) { 00262 case LDIF::Item: 00263 { 00264 name = d->ldif.attr(); 00265 value = d->ldif.val(); 00266 bool bFoundOC = name.lower() == "objectclass"; 00267 if( bFoundOC ) 00268 mCurrentObject.objectClass = QString::fromUtf8( value, value.size() ); 00269 if( mReportObjectClass || !bFoundOC ) 00270 mCurrentObject.attrs[ name ].append( value ); 00271 //kdDebug(5300) << "LdapClient::parseLDIF()" << name << " / " << value << endl; 00272 } 00273 break; 00274 case LDIF::EndEntry: 00275 finishCurrentObject(); 00276 break; 00277 default: 00278 break; 00279 } 00280 } while ( ret != LDIF::MoreData ); 00281 } 00282 00283 QString LdapClient::bindDN() const 00284 { 00285 return d->bindDN; 00286 } 00287 00288 QString LdapClient::pwdBindDN() const 00289 { 00290 return d->pwdBindDN; 00291 } 00292 00293 int LdapClient::clientNumber() const 00294 { 00295 return d->clientNumber; 00296 } 00297 00298 int LdapClient::completionWeight() const 00299 { 00300 return d->completionWeight; 00301 } 00302 00303 void KPIM::LdapClient::setCompletionWeight( int weight ) 00304 { 00305 d->completionWeight = weight; 00306 } 00307 00308 LdapSearch::LdapSearch() 00309 : mActiveClients( 0 ), mNoLDAPLookup( false ) 00310 { 00311 if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) { 00312 mNoLDAPLookup = true; 00313 return; 00314 } 00315 00316 readConfig(); 00317 connect(KDirWatch::self(), SIGNAL(dirty (const QString&)),this, 00318 SLOT(slotFileChanged(const QString&))); 00319 } 00320 00321 void LdapSearch::readConfig() 00322 { 00323 cancelSearch(); 00324 QValueList< LdapClient* >::Iterator it; 00325 for ( it = mClients.begin(); it != mClients.end(); ++it ) 00326 delete *it; 00327 mClients.clear(); 00328 00329 // stolen from KAddressBook 00330 KConfig config( "kabldaprc", true ); 00331 config.setGroup( "LDAP" ); 00332 int numHosts = config.readUnsignedNumEntry( "NumSelectedHosts"); 00333 if ( !numHosts ) { 00334 mNoLDAPLookup = true; 00335 } else { 00336 for ( int j = 0; j < numHosts; j++ ) { 00337 LdapClient* ldapClient = new LdapClient( j, this ); 00338 00339 QString host = config.readEntry( QString( "SelectedHost%1" ).arg( j ), "" ).stripWhiteSpace(); 00340 if ( !host.isEmpty() ) 00341 ldapClient->setHost( host ); 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 // note: we need "objectClass" to detect distribution lists 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 if ( file == mConfigFile ) 00387 readConfig(); 00388 } 00389 00390 void LdapSearch::startSearch( const QString& txt ) 00391 { 00392 if ( mNoLDAPLookup ) 00393 return; 00394 00395 cancelSearch(); 00396 00397 int pos = txt.find( '\"' ); 00398 if( pos >= 0 ) 00399 { 00400 ++pos; 00401 int pos2 = txt.find( '\"', pos ); 00402 if( pos2 >= 0 ) 00403 mSearchText = txt.mid( pos , pos2 - pos ); 00404 else 00405 mSearchText = txt.mid( pos ); 00406 } else 00407 mSearchText = txt; 00408 00409 QString filter = QString( "|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" ) 00410 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText ); 00411 00412 QValueList< LdapClient* >::Iterator it; 00413 for ( it = mClients.begin(); it != mClients.end(); ++it ) { 00414 (*it)->startQuery( filter ); 00415 kdDebug(5300) << "LdapSearch::startSearch() " << filter << endl; 00416 ++mActiveClients; 00417 } 00418 } 00419 00420 void LdapSearch::cancelSearch() 00421 { 00422 QValueList< LdapClient* >::Iterator it; 00423 for ( it = mClients.begin(); it != mClients.end(); ++it ) 00424 (*it)->cancelQuery(); 00425 00426 mActiveClients = 0; 00427 mResults.clear(); 00428 } 00429 00430 void LdapSearch::slotLDAPResult( const KPIM::LdapObject& obj ) 00431 { 00432 mResults.append( obj ); 00433 if ( !mDataTimer.isActive() ) 00434 mDataTimer.start( 500, true ); 00435 } 00436 00437 void LdapSearch::slotLDAPError( const QString& ) 00438 { 00439 slotLDAPDone(); 00440 } 00441 00442 void LdapSearch::slotLDAPDone() 00443 { 00444 if ( --mActiveClients > 0 ) 00445 return; 00446 00447 finish(); 00448 } 00449 00450 void LdapSearch::slotDataTimer() 00451 { 00452 QStringList lst; 00453 LdapResultList reslist; 00454 makeSearchData( lst, reslist ); 00455 if ( !lst.isEmpty() ) 00456 emit searchData( lst ); 00457 if ( !reslist.isEmpty() ) 00458 emit searchData( reslist ); 00459 } 00460 00461 void LdapSearch::finish() 00462 { 00463 mDataTimer.stop(); 00464 00465 slotDataTimer(); // emit final bunch of data 00466 emit searchDone(); 00467 } 00468 00469 void LdapSearch::makeSearchData( QStringList& ret, LdapResultList& resList ) 00470 { 00471 QString search_text_upper = mSearchText.upper(); 00472 00473 QValueList< KPIM::LdapObject >::ConstIterator it1; 00474 for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) { 00475 QString name, mail, givenname, sn; 00476 QStringList mails; 00477 bool isDistributionList = false; 00478 bool wasCN = false; 00479 bool wasDC = false; 00480 00481 kdDebug(5300) << "\n\nLdapSearch::makeSearchData()\n\n" << endl; 00482 00483 LdapAttrMap::ConstIterator it2; 00484 for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) { 00485 QByteArray val = (*it2).first(); 00486 int len = val.size(); 00487 if( '\0' == val[len-1] ) 00488 --len; 00489 const QString tmp = QString::fromUtf8( val, len ); 00490 kdDebug(5300) << " key: \"" << it2.key() << "\" value: \"" << tmp << "\"" << endl; 00491 if ( it2.key() == "cn" ) { 00492 name = tmp; 00493 if( mail.isEmpty() ) 00494 mail = tmp; 00495 else{ 00496 if( wasCN ) 00497 mail.prepend( "." ); 00498 else 00499 mail.prepend( "@" ); 00500 mail.prepend( tmp ); 00501 } 00502 wasCN = true; 00503 } else if ( it2.key() == "dc" ) { 00504 if( mail.isEmpty() ) 00505 mail = tmp; 00506 else{ 00507 if( wasDC ) 00508 mail.append( "." ); 00509 else 00510 mail.append( "@" ); 00511 mail.append( tmp ); 00512 } 00513 wasDC = true; 00514 } else if( it2.key() == "mail" ) { 00515 mail = tmp; 00516 LdapAttrValue::ConstIterator it3 = it2.data().begin(); 00517 for ( ; it3 != it2.data().end(); ++it3 ) { 00518 mails.append( QString::fromUtf8( (*it3).data(), (*it3).size() ) ); 00519 } 00520 } else if( it2.key() == "givenName" ) 00521 givenname = tmp; 00522 else if( it2.key() == "sn" ) 00523 sn = tmp; 00524 else if( it2.key() == "objectClass" && tmp == "groupOfNames" ) { 00525 isDistributionList = true; 00526 } 00527 } 00528 00529 if( mails.isEmpty()) { 00530 if ( !mail.isEmpty() ) mails.append( mail ); 00531 if( isDistributionList ) { 00532 kdDebug(5300) << "\n\nLdapSearch::makeSearchData() found a list: " << name << "\n\n" << endl; 00533 ret.append( name ); 00534 // following lines commented out for bugfixing kolab issue #177: 00535 // 00536 // Unlike we thought previously we may NOT append the server name here. 00537 // 00538 // The right server is found by the SMTP server instead: Kolab users 00539 // must use the correct SMTP server, by definition. 00540 // 00541 //mail = (*it1).client->base().simplifyWhiteSpace(); 00542 //mail.replace( ",dc=", ".", false ); 00543 //if( mail.startsWith("dc=", false) ) 00544 // mail.remove(0, 3); 00545 //mail.prepend( '@' ); 00546 //mail.prepend( name ); 00547 //mail = name; 00548 } else { 00549 kdDebug(5300) << "LdapSearch::makeSearchData() found BAD ENTRY: \"" << name << "\"" << endl; 00550 continue; // nothing, bad entry 00551 } 00552 } else if ( name.isEmpty() ) { 00553 kdDebug(5300) << "LdapSearch::makeSearchData() mail: \"" << mail << "\"" << endl; 00554 ret.append( mail ); 00555 } else { 00556 kdDebug(5300) << "LdapSearch::makeSearchData() name: \"" << name << "\" mail: \"" << mail << "\"" << endl; 00557 ret.append( QString( "%1 <%2>" ).arg( name ).arg( mail ) ); 00558 } 00559 00560 LdapResult sr; 00561 sr.clientNumber = (*it1).client->clientNumber(); 00562 sr.completionWeight = (*it1).client->completionWeight(); 00563 sr.name = name; 00564 sr.email = mails; 00565 resList.append( sr ); 00566 } 00567 00568 mResults.clear(); 00569 } 00570 00571 bool LdapSearch::isAvailable() const 00572 { 00573 return !mNoLDAPLookup; 00574 } 00575 00576 #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 Aug 27 12:50:25 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003