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