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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <kmdcodec.h> 00036 #include <kprotocolinfo.h> 00037 00038 #include "ldapclient.h" 00039 #include "ldif.h" 00040 #include "ldapurl.h" 00041 00042 using namespace KABC; 00043 00044 class LdapClient::LdapClientPrivate{ 00045 public: 00046 QString bindDN; 00047 QString pwdBindDN; 00048 LDIF ldif; 00049 }; 00050 00051 QString LdapObject::toString() const 00052 { 00053 QString result = QString::fromLatin1( "\ndn: %1\n" ).arg( dn ); 00054 for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) { 00055 QString attr = it.key(); 00056 for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) { 00057 result += QString::fromUtf8( LDIF::assembleLine( attr, *it2, 76 ) ) + "\n"; 00058 } 00059 } 00060 00061 return result; 00062 } 00063 00064 void LdapObject::clear() 00065 { 00066 dn = QString::null; 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( QObject* parent, const char* name ) 00080 : QObject( parent, name ), mJob( 0 ), mActive( false ) 00081 { 00082 d = new LdapClientPrivate; 00083 } 00084 00085 LdapClient::~LdapClient() 00086 { 00087 cancelQuery(); 00088 delete d; d = 0; 00089 } 00090 00091 void LdapClient::setHost( const QString& host ) 00092 { 00093 mHost = host; 00094 } 00095 00096 void LdapClient::setPort( const QString& port ) 00097 { 00098 mPort = port; 00099 } 00100 00101 void LdapClient::setBase( const QString& base ) 00102 { 00103 mBase = base; 00104 } 00105 00106 void LdapClient::setBindDN( const QString& bindDN ) 00107 { 00108 d->bindDN = bindDN; 00109 } 00110 00111 void LdapClient::setPwdBindDN( const QString& pwdBindDN ) 00112 { 00113 d->pwdBindDN = pwdBindDN; 00114 } 00115 00116 void LdapClient::setAttrs( const QStringList& attrs ) 00117 { 00118 mAttrs = attrs; 00119 } 00120 00121 void LdapClient::startQuery( const QString& filter ) 00122 { 00123 cancelQuery(); 00124 LDAPUrl url; 00125 00126 url.setProtocol( "ldap" ); 00127 url.setUser( d->bindDN ); 00128 url.setPass( d->pwdBindDN ); 00129 url.setHost( mHost ); 00130 url.setPort( mPort.toUInt() ); 00131 url.setDn( mBase ); 00132 url.setAttributes( mAttrs ); 00133 url.setScope( mScope == "one" ? LDAPUrl::One : LDAPUrl::Sub ); 00134 url.setFilter( "("+filter+")" ); 00135 00136 kdDebug(5700) << "Doing query: " << url.prettyURL() << endl; 00137 00138 startParseLDIF(); 00139 mActive = true; 00140 mJob = KIO::get( url, false, false ); 00141 connect( mJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ), 00142 this, SLOT( slotData( KIO::Job*, const QByteArray& ) ) ); 00143 connect( mJob, SIGNAL( infoMessage( KIO::Job*, const QString& ) ), 00144 this, SLOT( slotInfoMessage( KIO::Job*, const QString& ) ) ); 00145 connect( mJob, SIGNAL( result( KIO::Job* ) ), 00146 this, SLOT( slotDone() ) ); 00147 } 00148 00149 void LdapClient::cancelQuery() 00150 { 00151 if ( mJob ) { 00152 mJob->kill(); 00153 mJob = 0; 00154 } 00155 00156 mActive = false; 00157 } 00158 00159 void LdapClient::slotData( KIO::Job*, const QByteArray& data ) 00160 { 00161 #ifndef NDEBUG // don't create the QString 00162 // QString str( data ); 00163 // kdDebug(5700) << "LdapClient: Got \"" << str << "\"\n"; 00164 #endif 00165 parseLDIF( data ); 00166 } 00167 00168 void LdapClient::slotInfoMessage( KIO::Job*, const QString & ) 00169 { 00170 //qDebug("Job said \"%s\"", info.latin1()); 00171 } 00172 00173 void LdapClient::slotDone() 00174 { 00175 endParseLDIF(); 00176 mActive = false; 00177 #if 0 00178 for ( QValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) { 00179 qDebug( (*it).toString().latin1() ); 00180 } 00181 #endif 00182 int err = mJob->error(); 00183 if ( err && err != KIO::ERR_USER_CANCELED ) { 00184 emit error( KIO::buildErrorString( err, QString("%1:%2").arg( mHost ).arg( mPort ) ) ); 00185 } 00186 emit done(); 00187 } 00188 00189 void LdapClient::startParseLDIF() 00190 { 00191 mCurrentObject.clear(); 00192 mLastAttrName = 0; 00193 mLastAttrValue = 0; 00194 mIsBase64 = false; 00195 d->ldif.startParsing(); 00196 } 00197 00198 void LdapClient::endParseLDIF() 00199 { 00200 } 00201 00202 void LdapClient::parseLDIF( const QByteArray& data ) 00203 { 00204 if ( data.size() ) { 00205 d->ldif.setLDIF( data ); 00206 } else { 00207 d->ldif.endLDIF(); 00208 } 00209 00210 LDIF::ParseVal ret; 00211 QString name; 00212 do { 00213 ret = d->ldif.nextItem(); 00214 switch ( ret ) { 00215 case LDIF::Item: 00216 { 00217 name = d->ldif.attr(); 00218 // Must make a copy! QByteArray is explicitely shared 00219 QByteArray value = d->ldif.val().copy(); 00220 mCurrentObject.attrs[ name ].append( value ); 00221 break; 00222 } 00223 case LDIF::EndEntry: 00224 mCurrentObject.dn = d->ldif.dn(); 00225 mCurrentObject.client = this; 00226 emit result( mCurrentObject ); 00227 mCurrentObject.clear(); 00228 break; 00229 default: 00230 break; 00231 } 00232 } while ( ret != LDIF::MoreData ); 00233 } 00234 00235 QString LdapClient::bindDN() const 00236 { 00237 return d->bindDN; 00238 } 00239 00240 QString LdapClient::pwdBindDN() const 00241 { 00242 return d->pwdBindDN; 00243 } 00244 00245 LdapSearch::LdapSearch() 00246 : mActiveClients( 0 ), mNoLDAPLookup( false ) 00247 { 00248 if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) { 00249 mNoLDAPLookup = true; 00250 return; 00251 } 00252 00253 // stolen from KAddressBook 00254 KConfig config( "kabldaprc", true ); 00255 config.setGroup( "LDAP" ); 00256 int numHosts = config.readUnsignedNumEntry( "NumSelectedHosts"); 00257 if ( !numHosts ) { 00258 mNoLDAPLookup = true; 00259 return; 00260 } else { 00261 for ( int j = 0; j < numHosts; j++ ) { 00262 LdapClient* ldapClient = new LdapClient( this ); 00263 00264 QString host = config.readEntry( QString( "SelectedHost%1" ).arg( j ), "" ).stripWhiteSpace(); 00265 if ( !host.isEmpty() ) 00266 ldapClient->setHost( host ); 00267 00268 QString port = QString::number( config.readUnsignedNumEntry( QString( "SelectedPort%1" ).arg( j ) ) ); 00269 if ( !port.isEmpty() ) 00270 ldapClient->setPort( port ); 00271 00272 QString base = config.readEntry( QString( "SelectedBase%1" ).arg( j ), "" ).stripWhiteSpace(); 00273 if ( !base.isEmpty() ) 00274 ldapClient->setBase( base ); 00275 00276 QString bindDN = config.readEntry( QString( "SelectedBind%1" ).arg( j ) ).stripWhiteSpace(); 00277 if ( !bindDN.isEmpty() ) 00278 ldapClient->setBindDN( bindDN ); 00279 00280 QString pwdBindDN = config.readEntry( QString( "SelectedPwdBind%1" ).arg( j ) ); 00281 if ( !pwdBindDN.isEmpty() ) 00282 ldapClient->setPwdBindDN( pwdBindDN ); 00283 00284 QStringList attrs; 00285 attrs << "cn" << "mail" << "givenname" << "sn"; 00286 ldapClient->setAttrs( attrs ); 00287 00288 connect( ldapClient, SIGNAL( result( const KABC::LdapObject& ) ), 00289 this, SLOT( slotLDAPResult( const KABC::LdapObject& ) ) ); 00290 connect( ldapClient, SIGNAL( done() ), 00291 this, SLOT( slotLDAPDone() ) ); 00292 connect( ldapClient, SIGNAL( error( const QString& ) ), 00293 this, SLOT( slotLDAPError( const QString& ) ) ); 00294 00295 mClients.append( ldapClient ); 00296 } 00297 } 00298 00299 connect( &mDataTimer, SIGNAL( timeout() ), SLOT( slotDataTimer() ) ); 00300 } 00301 00302 void LdapSearch::startSearch( const QString& txt ) 00303 { 00304 if ( mNoLDAPLookup ) 00305 return; 00306 00307 cancelSearch(); 00308 00309 int pos = txt.find( '\"' ); 00310 if( pos >= 0 ) 00311 { 00312 ++pos; 00313 int pos2 = txt.find( '\"', pos ); 00314 if( pos2 >= 0 ) 00315 mSearchText = txt.mid( pos , pos2 - pos ); 00316 else 00317 mSearchText = txt.mid( pos ); 00318 } else 00319 mSearchText = txt; 00320 00321 QString filter = QString( "|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" ) 00322 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText ); 00323 00324 QValueList< LdapClient* >::Iterator it; 00325 for ( it = mClients.begin(); it != mClients.end(); ++it ) { 00326 (*it)->startQuery( filter ); 00327 ++mActiveClients; 00328 } 00329 } 00330 00331 void LdapSearch::cancelSearch() 00332 { 00333 QValueList< LdapClient* >::Iterator it; 00334 for ( it = mClients.begin(); it != mClients.end(); ++it ) 00335 (*it)->cancelQuery(); 00336 00337 mActiveClients = 0; 00338 mResults.clear(); 00339 } 00340 00341 void LdapSearch::slotLDAPResult( const KABC::LdapObject& obj ) 00342 { 00343 mResults.append( obj ); 00344 if ( !mDataTimer.isActive() ) 00345 mDataTimer.start( 500, true ); 00346 } 00347 00348 void LdapSearch::slotLDAPError( const QString& ) 00349 { 00350 slotLDAPDone(); 00351 } 00352 00353 void LdapSearch::slotLDAPDone() 00354 { 00355 if ( --mActiveClients > 0 ) 00356 return; 00357 00358 finish(); 00359 } 00360 00361 void LdapSearch::slotDataTimer() 00362 { 00363 QStringList lst; 00364 LdapResultList reslist; 00365 makeSearchData( lst, reslist ); 00366 if ( !lst.isEmpty() ) 00367 emit searchData( lst ); 00368 if ( !reslist.isEmpty() ) 00369 emit searchData( reslist ); 00370 } 00371 00372 void LdapSearch::finish() 00373 { 00374 mDataTimer.stop(); 00375 00376 slotDataTimer(); // emit final bunch of data 00377 emit searchDone(); 00378 } 00379 00380 void LdapSearch::makeSearchData( QStringList& ret, LdapResultList& resList ) 00381 { 00382 QString search_text_upper = mSearchText.upper(); 00383 00384 QValueList< KABC::LdapObject >::ConstIterator it1; 00385 for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) { 00386 QString name, mail, givenname, sn; 00387 00388 LdapAttrMap::ConstIterator it2; 00389 for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) { 00390 QString tmp = QString::fromUtf8( (*it2).first(), (*it2).first().size() ); 00391 if ( it2.key() == "cn" ) 00392 name = tmp; // TODO loop? 00393 else if( it2.key() == "mail" ) 00394 mail = tmp; 00395 else if( it2.key() == "givenName" ) 00396 givenname = tmp; 00397 else if( it2.key() == "sn" ) 00398 sn = tmp; 00399 } 00400 00401 if( mail.isEmpty()) 00402 continue; // nothing, bad entry 00403 else if ( name.isEmpty() ) 00404 ret.append( mail ); 00405 else { 00406 kdDebug(5700) << "<" << name << "><" << mail << ">" << endl; 00407 ret.append( QString( "%1 <%2>" ).arg( name ).arg( mail ) ); 00408 } 00409 00410 LdapResult sr; 00411 sr.clientNumber = mClients.findIndex( (*it1).client ); 00412 sr.name = name; 00413 sr.email = mail; 00414 resList.append( sr ); 00415 } 00416 00417 mResults.clear(); 00418 } 00419 00420 bool LdapSearch::isAvailable() const 00421 { 00422 return !mNoLDAPLookup; 00423 } 00424 00425 00426 00427 #include "ldapclient.moc"