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 <kmdcodec.h>
00036 #include <kprotocolinfo.h>
00037
00038 #include "ldapclient.h"
00039
00040 using namespace KABC;
00041
00042 class LdapClient::LdapClientPrivate{
00043 public:
00044 QString bindDN;
00045 QString pwdBindDN;
00046 };
00047
00048 QString LdapObject::toString() const
00049 {
00050 QString result = QString::fromLatin1( "\ndn: %1\n" ).arg( dn );
00051 for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) {
00052 QString attr = it.key();
00053 for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) {
00054 if ( attr == "jpegPhoto" ) {
00055 QByteArray buf = *it2;
00056 #if 0
00057 qDebug( "Trying to load image from buf with size %d", (*it2).size() );
00058 QPixmap pix;
00059 pix.loadFromData( buf, "JPEG" );
00060 qDebug( "Image loaded successfully" );
00061 QLabel* l = new QLabel( 0 );
00062 QFile f( "tmp.jpg" );
00063 f.open( IO_WriteOnly );
00064 f.writeBlock( buf );
00065 f.close();
00066
00067
00068 #endif
00069 } else {
00070 result += QString("%1: %2\n").arg(attr).arg(QString::fromUtf8(*it2));
00071 }
00072 }
00073 }
00074
00075 return result;
00076 }
00077
00078 void LdapObject::clear()
00079 {
00080 dn = QString::null;
00081 attrs.clear();
00082 }
00083
00084 void LdapObject::assign( const LdapObject& that )
00085 {
00086 if ( &that != this ) {
00087 dn = that.dn;
00088 attrs = that.attrs;
00089 }
00090 }
00091
00092 LdapClient::LdapClient( QObject* parent, const char* name )
00093 : QObject( parent, name ), mJob( 0 ), mActive( false )
00094 {
00095 d = new LdapClientPrivate;
00096 }
00097
00098 LdapClient::~LdapClient()
00099 {
00100 cancelQuery();
00101 delete d; d = 0;
00102 }
00103
00104 void LdapClient::setHost( const QString& host )
00105 {
00106 mHost = host;
00107 }
00108
00109 void LdapClient::setPort( const QString& port )
00110 {
00111 mPort = port;
00112 }
00113
00114 void LdapClient::setBase( const QString& base )
00115 {
00116 mBase = base;
00117 }
00118
00119 void LdapClient::setBindDN( const QString& bindDN )
00120 {
00121 d->bindDN = bindDN;
00122 }
00123
00124 void LdapClient::setPwdBindDN( const QString& pwdBindDN )
00125 {
00126 d->pwdBindDN = pwdBindDN;
00127 }
00128
00129 void LdapClient::setAttrs( const QStringList& attrs )
00130 {
00131 mAttrs = attrs;
00132 }
00133
00134 void LdapClient::startQuery( const QString& filter )
00135 {
00136 cancelQuery();
00137 QString query;
00138 if ( mScope.isEmpty() )
00139 mScope = "sub";
00140
00141 QString auth;
00142 QString encodedPassword;
00143 if ( !d->bindDN.isEmpty() ) {
00144 auth = d->bindDN;
00145 QUrl::encode( auth );
00146 if ( !d->pwdBindDN.isEmpty() ) {
00147 encodedPassword = d->pwdBindDN;
00148 QUrl::encode( encodedPassword );
00149 auth += ":" + encodedPassword;
00150 }
00151 auth += "@";
00152 }
00153
00154 QString host = mHost;
00155 if ( !mPort.isEmpty() ) {
00156 host += ':';
00157 host += mPort;
00158 }
00159
00160 if ( mAttrs.empty() ) {
00161 QTextOStream(&query) << "ldap://" << auth << host << "/" << mBase << "?*?" << mScope << "?(" << filter << ")";
00162 } else {
00163 QTextOStream(&query) << "ldap://" << auth << host << "/" << mBase << "?" << mAttrs.join(",") << "?" << mScope << "?(" << filter << ")";
00164 }
00165 kdDebug(5700) << "Doing query " << query << endl;
00166
00167 startParseLDIF();
00168 mActive = true;
00169 mJob = KIO::get( KURL( query ), false, false );
00170 connect( mJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
00171 this, SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
00172 connect( mJob, SIGNAL( infoMessage( KIO::Job*, const QString& ) ),
00173 this, SLOT( slotInfoMessage( KIO::Job*, const QString& ) ) );
00174 connect( mJob, SIGNAL( result( KIO::Job* ) ),
00175 this, SLOT( slotDone() ) );
00176 }
00177
00178 void LdapClient::cancelQuery()
00179 {
00180 if ( mJob ) {
00181 mJob->kill();
00182 mJob = 0;
00183 }
00184
00185 mActive = false;
00186 }
00187
00188 void LdapClient::slotData( KIO::Job*, const QByteArray& data )
00189 {
00190 #ifndef NDEBUG // don't create the QString
00191 QString str( data );
00192 kdDebug(5700) << "Got \"" << str.latin1() << "\"\n";
00193 #endif
00194 parseLDIF( data );
00195 }
00196
00197 void LdapClient::slotInfoMessage( KIO::Job*, const QString & )
00198 {
00199
00200 }
00201
00202 void LdapClient::slotDone()
00203 {
00204 endParseLDIF();
00205 mActive = false;
00206 #if 0
00207 for ( QValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) {
00208 qDebug( (*it).toString().latin1() );
00209 }
00210 #endif
00211 int err = mJob->error();
00212 if ( err ) {
00213 emit error( KIO::buildErrorString( err, QString("%1:%2").arg( mHost ).arg( mPort ) ) );
00214 }
00215 emit done();
00216 }
00217
00218 void LdapClient::startParseLDIF()
00219 {
00220 mCurrentObject.clear();
00221 mLastAttrName = 0;
00222 mLastAttrValue = 0;
00223 mIsBase64 = false;
00224 }
00225
00226 void LdapClient::endParseLDIF()
00227 {
00228 if ( !mCurrentObject.dn.isEmpty() ) {
00229 if ( !mLastAttrName.isNull() && !mLastAttrValue.isNull() ) {
00230 if ( mIsBase64 ) {
00231 QByteArray out;
00232 KCodecs::base64Decode( mLastAttrValue, out );
00233
00234 mCurrentObject.attrs[ mLastAttrName ].append( out );
00235 } else {
00236 mCurrentObject.attrs[ mLastAttrName ].append( mLastAttrValue );
00237 }
00238 }
00239 emit result( mCurrentObject );
00240 }
00241 }
00242
00243 void LdapClient::parseLDIF( const QByteArray& data )
00244 {
00245
00246 if ( data.isEmpty() )
00247 return;
00248 mBuf += QCString( data, data.size() + 1 );
00249 int nl;
00250 while ( (nl = mBuf.find('\n')) != -1 ) {
00251
00252
00253
00254
00255 QCString line = mBuf.left( nl );
00256 if ( mBuf.length() > (unsigned int)(nl+1) )
00257 mBuf = mBuf.mid( nl+1 );
00258 else
00259 mBuf = "";
00260
00261 if ( line.length() > 0 ) {
00262 if ( line[ 0 ] == '#' ) {
00263 continue;
00264 } else if ( line[ 0 ] == ' ' || line[ 0 ] == '\t' ) {
00265 line = line.stripWhiteSpace();
00266
00267 mLastAttrValue += line;
00268 continue;
00269 }
00270 } else
00271 continue;
00272
00273 int colon = line.find(':');
00274 if ( colon != -1 ) {
00275 if ( mLastAttrName == "dn" ) {
00276 if ( !mCurrentObject.dn.isNull() ) {
00277 emit result( mCurrentObject );
00278 mCurrentObject.clear();
00279 }
00280 mCurrentObject.dn = mLastAttrValue;
00281 mLastAttrValue = 0;
00282 mLastAttrName = 0;
00283 } else if ( !mLastAttrName.isEmpty() ) {
00284
00285 if ( mIsBase64 ) {
00286 QByteArray out;
00287 KCodecs::base64Decode( mLastAttrValue, out );
00288
00289 mCurrentObject.attrs[ mLastAttrName ].append( out );
00290 } else {
00291 mCurrentObject.attrs[ mLastAttrName ].append( mLastAttrValue );
00292 }
00293 }
00294
00295 mLastAttrName = line.left( colon ).stripWhiteSpace();
00296
00297 ++colon;
00298 if ( line[colon] == ':' ) {
00299 mIsBase64 = true;
00300
00301 ++colon;
00302 } else {
00303
00304 mIsBase64 = false;
00305 }
00306
00307 mLastAttrValue = line.mid( colon ).stripWhiteSpace();
00308 }
00309 }
00310 }
00311
00312 QString LdapClient::bindDN() const
00313 {
00314 return d->bindDN;
00315 }
00316
00317 QString LdapClient::pwdBindDN() const
00318 {
00319 return d->pwdBindDN;
00320 }
00321
00322 LdapSearch::LdapSearch()
00323 : mActiveClients( 0 ), mNoLDAPLookup( false )
00324 {
00325 if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) {
00326 mNoLDAPLookup = true;
00327 return;
00328 }
00329
00330
00331 KConfig config( "kabldaprc", true );
00332 config.setGroup( "LDAP" );
00333 int numHosts = config.readUnsignedNumEntry( "NumSelectedHosts");
00334 if ( !numHosts ) {
00335 mNoLDAPLookup = true;
00336 return;
00337 } else {
00338 for ( int j = 0; j < numHosts; j++ ) {
00339 LdapClient* ldapClient = new LdapClient( this );
00340
00341 QString host = config.readEntry( QString( "SelectedHost%1" ).arg( j ), "" ).stripWhiteSpace();
00342 if ( !host.isEmpty() )
00343 ldapClient->setHost( host );
00344
00345 QString port = QString::number( config.readUnsignedNumEntry( QString( "SelectedPort%1" ).arg( j ) ) );
00346 if ( !port.isEmpty() )
00347 ldapClient->setPort( port );
00348
00349 QString base = config.readEntry( QString( "SelectedBase%1" ).arg( j ), "" ).stripWhiteSpace();
00350 if ( !base.isEmpty() )
00351 ldapClient->setBase( base );
00352
00353 QString bindDN = config.readEntry( QString( "SelectedBind%1" ).arg( j ) ).stripWhiteSpace();
00354 if ( !bindDN.isEmpty() )
00355 ldapClient->setBindDN( bindDN );
00356
00357 QString pwdBindDN = config.readEntry( QString( "SelectedPwdBind%1" ).arg( j ) ).stripWhiteSpace();
00358 if ( !pwdBindDN.isEmpty() )
00359 ldapClient->setPwdBindDN( pwdBindDN );
00360
00361 QStringList attrs;
00362 attrs << "cn" << "mail" << "givenname" << "sn";
00363 ldapClient->setAttrs( attrs );
00364
00365 connect( ldapClient, SIGNAL( result( const KABC::LdapObject& ) ),
00366 this, SLOT( slotLDAPResult( const KABC::LdapObject& ) ) );
00367 connect( ldapClient, SIGNAL( done() ),
00368 this, SLOT( slotLDAPDone() ) );
00369 connect( ldapClient, SIGNAL( error( const QString& ) ),
00370 this, SLOT( slotLDAPError( const QString& ) ) );
00371
00372 mClients.append( ldapClient );
00373 }
00374 }
00375
00376 connect( &mDataTimer, SIGNAL( timeout() ), SLOT( slotDataTimer() ) );
00377 }
00378
00379 void LdapSearch::startSearch( const QString& txt )
00380 {
00381 if ( mNoLDAPLookup )
00382 return;
00383
00384 cancelSearch();
00385
00386 int pos = txt.find( '\"' );
00387 if( pos >= 0 )
00388 {
00389 ++pos;
00390 int pos2 = txt.find( '\"', pos );
00391 if( pos2 >= 0 )
00392 mSearchText = txt.mid( pos , pos2 - pos );
00393 else
00394 mSearchText = txt.mid( pos );
00395 } else
00396 mSearchText = txt;
00397
00398 QString filter = QString( "|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" )
00399 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText );
00400
00401 QValueList< LdapClient* >::Iterator it;
00402 for ( it = mClients.begin(); it != mClients.end(); ++it ) {
00403 (*it)->startQuery( filter );
00404 ++mActiveClients;
00405 }
00406 }
00407
00408 void LdapSearch::cancelSearch()
00409 {
00410 QValueList< LdapClient* >::Iterator it;
00411 for ( it = mClients.begin(); it != mClients.end(); ++it )
00412 (*it)->cancelQuery();
00413
00414 mActiveClients = 0;
00415 mResults.clear();
00416 }
00417
00418 void LdapSearch::slotLDAPResult( const KABC::LdapObject& obj )
00419 {
00420 mResults.append( obj );
00421 if ( !mDataTimer.isActive() )
00422 mDataTimer.start( 500, true );
00423 }
00424
00425 void LdapSearch::slotLDAPError( const QString& )
00426 {
00427 slotLDAPDone();
00428 }
00429
00430 void LdapSearch::slotLDAPDone()
00431 {
00432 if ( --mActiveClients > 0 )
00433 return;
00434
00435 finish();
00436 }
00437
00438 void LdapSearch::slotDataTimer()
00439 {
00440 emit searchData( makeSearchData() );
00441 }
00442
00443 void LdapSearch::finish()
00444 {
00445 mDataTimer.stop();
00446
00447 emit searchData( makeSearchData() );
00448 emit searchDone();
00449 }
00450
00451 QStringList LdapSearch::makeSearchData()
00452 {
00453 QStringList ret;
00454 QString search_text_upper = mSearchText.upper();
00455
00456 QValueList< KABC::LdapObject >::ConstIterator it1;
00457 for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) {
00458 QString name, mail, givenname, sn;
00459
00460 LdapAttrMap::ConstIterator it2;
00461 for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) {
00462 QString tmp = QString::fromUtf8((*it2).first());
00463 if ( it2.key() == "cn" )
00464 name = tmp;
00465 else if( it2.key() == "mail" )
00466 mail = tmp;
00467 else if( it2.key() == "givenName" )
00468 givenname = tmp;
00469 else if( it2.key() == "sn" )
00470 sn = tmp;
00471 }
00472
00473 if( mail.isEmpty())
00474 ;
00475 else if ( name.isEmpty() )
00476 ret.append( mail );
00477 else {
00478 kdDebug(5700) << "<" << name << "><" << mail << ">" << endl;
00479 ret.append( QString( "%1 <%2>" ).arg( name ).arg( mail ) );
00480 #if 0
00481
00482 if ( givenname.upper().startsWith( search_text_upper ) )
00483 ret.append( QString( "$$%1$%2 <%3>" ).arg( givenname ).arg( name ).arg( mail ) );
00484 if ( sn.upper().startsWith( search_text_upper ) )
00485 ret.append( QString( "$$%1$%2 <%3>" ).arg( sn ).arg( name ).arg( mail ) );
00486 #endif
00487 }
00488 }
00489
00490 mResults.clear();
00491
00492 return ret;
00493 }
00494
00495 bool LdapSearch::isAvailable() const
00496 {
00497 return !mNoLDAPLookup;
00498 }
00499
00500
00501
00502 #include "ldapclient.moc"