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 += "@";
}
QString host = mHost;
if ( !mPort.isEmpty() ) {
host += ':';
host += mPort;
}
if ( mAttrs.empty() ) {
query = QString("ldap://%1%2/%3?*?%4?(%5)").arg( auth ).arg( host ).arg( mBase ).arg( mScope ).arg( filter );
00144 } else {
00145 query = QString("ldap://%1%2/%3?%4?%5?(%6)").arg( auth ).arg( host ).arg( mBase )
00146 .arg( mAttrs.join(",") ).arg( mScope ).arg( filter );
00147 }
00148 kdDebug(5700) << "Doing query " << query << endl;
00149
00150 startParseLDIF();
00151 mActive = true;
00152 mJob = KIO::get( KURL( query ), false, false );
00153 connect( mJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
00154 this, SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
00155 connect( mJob, SIGNAL( infoMessage( KIO::Job*, const QString& ) ),
00156 this, SLOT( slotInfoMessage( KIO::Job*, const QString& ) ) );
00157 connect( mJob, SIGNAL( result( KIO::Job* ) ),
00158 this, SLOT( slotDone() ) );
00159 }
00160
00161 void LdapClient::cancelQuery()
00162 {
00163 if ( mJob ) {
00164 mJob->kill();
00165 mJob = 0;
00166 }
00167
00168 mActive = false;
00169 }
00170
00171 void LdapClient::slotData( KIO::Job*, const QByteArray& data )
00172 {
00173 #ifndef NDEBUG // don't create the QString
00174 QString str( data );
00175 kdDebug(5700) << "Got \"" << str.latin1() << "\"\n";
00176 #endif
00177 parseLDIF( data );
00178 }
00179
00180 void LdapClient::slotInfoMessage( KIO::Job*, const QString & )
00181 {
00182
00183 }
00184
00185 void LdapClient::slotDone()
00186 {
00187 endParseLDIF();
00188 mActive = false;
00189 #if 0
00190 for ( QValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) {
00191 qDebug( (*it).toString().latin1() );
00192 }
00193 #endif
00194 int err = mJob->error();
00195 if ( err ) {
00196 emit error( KIO::buildErrorString( err, QString("%1:%2").arg( mHost ).arg( mPort ) ) );
00197 }
00198 emit done();
00199 }
00200
00201 void LdapClient::startParseLDIF()
00202 {
00203 mCurrentObject.clear();
00204 mLastAttrName = 0;
00205 mLastAttrValue = 0;
00206 mIsBase64 = false;
00207 }
00208
00209 void LdapClient::endParseLDIF()
00210 {
00211 if ( !mCurrentObject.dn.isEmpty() ) {
00212 if ( !mLastAttrName.isNull() && !mLastAttrValue.isNull() ) {
00213 if ( mIsBase64 ) {
00214 QByteArray out;
00215 KCodecs::base64Decode( mLastAttrValue, out );
00216
00217 mCurrentObject.attrs[ mLastAttrName ].append( out );
00218 } else {
00219 mCurrentObject.attrs[ mLastAttrName ].append( mLastAttrValue );
00220 }
00221 }
00222 emit result( mCurrentObject );
00223 }
00224 }
00225
00226 void LdapClient::parseLDIF( const QByteArray& data )
00227 {
00228
00229 if ( data.isEmpty() )
00230 return;
00231 mBuf += QCString( data, data.size() + 1 );
00232 int nl;
00233 while ( (nl = mBuf.find('\n')) != -1 ) {
00234
00235
00236
00237
00238 QCString line = mBuf.left( nl );
00239 if ( mBuf.length() > (unsigned int)(nl+1) )
00240 mBuf = mBuf.mid( nl+1 );
00241 else
00242 mBuf = "";
00243
00244 if ( line.length() > 0 ) {
00245 if ( line[ 0 ] == '#' ) {
00246 continue;
00247 } else if ( line[ 0 ] == ' ' || line[ 0 ] == '\t' ) {
00248 line = line.stripWhiteSpace();
00249
00250 mLastAttrValue += line;
00251 continue;
00252 }
00253 } else
00254 continue;
00255
00256 int colon = line.find(':');
00257 if ( colon != -1 ) {
00258 if ( mLastAttrName == "dn" ) {
00259 if ( !mCurrentObject.dn.isNull() ) {
00260 emit result( mCurrentObject );
00261 mCurrentObject.clear();
00262 }
00263 mCurrentObject.dn = mLastAttrValue;
00264 mLastAttrValue = 0;
00265 mLastAttrName = 0;
00266 } else if ( !mLastAttrName.isEmpty() ) {
00267
00268 if ( mIsBase64 ) {
00269 QByteArray out;
00270 KCodecs::base64Decode( mLastAttrValue, out );
00271
00272 mCurrentObject.attrs[ mLastAttrName ].append( out );
00273 } else {
00274 mCurrentObject.attrs[ mLastAttrName ].append( mLastAttrValue );
00275 }
00276 }
00277
00278 mLastAttrName = line.left( colon ).stripWhiteSpace();
00279
00280 ++colon;
00281 if ( line[colon] == ':' ) {
00282 mIsBase64 = true;
00283
00284 ++colon;
00285 } else {
00286
00287 mIsBase64 = false;
00288 }
00289
00290 mLastAttrValue = line.mid( colon ).stripWhiteSpace();
00291 }
00292 }
00293 }
00294
00295 QString LdapClient::bindDN() const
00296 {
00297 return d->bindDN;
00298 }
00299
00300 QString LdapClient::pwdBindDN() const
00301 {
00302 return d->pwdBindDN;
00303 }
00304
00305 LdapSearch::LdapSearch()
00306 : mActiveClients( 0 ), mNoLDAPLookup( false )
00307 {
00308 if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) {
00309 mNoLDAPLookup = true;
00310 return;
00311 }
00312
00313
00314 KConfig config( "kabldaprc", true );
00315 config.setGroup( "LDAP" );
00316 int numHosts = config.readUnsignedNumEntry( "NumSelectedHosts");
00317 if ( !numHosts ) {
00318 mNoLDAPLookup = true;
00319 return;
00320 } else {
00321 for ( int j = 0; j < numHosts; j++ ) {
00322 LdapClient* ldapClient = new LdapClient( this );
00323
00324 QString host = config.readEntry( QString( "SelectedHost%1" ).arg( j ), "" ).stripWhiteSpace();
00325 if ( !host.isEmpty() )
00326 ldapClient->setHost( host );
00327
00328 QString port = QString::number( config.readUnsignedNumEntry( QString( "SelectedPort%1" ).arg( j ) ) );
00329 if ( !port.isEmpty() )
00330 ldapClient->setPort( port );
00331
00332 QString base = config.readEntry( QString( "SelectedBase%1" ).arg( j ), "" ).stripWhiteSpace();
00333 if ( !base.isEmpty() )
00334 ldapClient->setBase( base );
00335
00336 QString bindDN = config.readEntry( QString( "SelectedBind%1" ).arg( j ) ).stripWhiteSpace();
00337 if ( !bindDN.isEmpty() )
00338 ldapClient->setBindDN( bindDN );
00339
00340 QString pwdBindDN = config.readEntry( QString( "SelectedPwdBind%1" ).arg( j ) ).stripWhiteSpace();
00341 if ( !pwdBindDN.isEmpty() )
00342 ldapClient->setPwdBindDN( pwdBindDN );
00343
00344 QStringList attrs;
00345 attrs << "cn" << "mail" << "givenname" << "sn";
00346 ldapClient->setAttrs( attrs );
00347
00348 connect( ldapClient, SIGNAL( result( const KABC::LdapObject& ) ),
00349 this, SLOT( slotLDAPResult( const KABC::LdapObject& ) ) );
00350 connect( ldapClient, SIGNAL( done() ),
00351 this, SLOT( slotLDAPDone() ) );
00352 connect( ldapClient, SIGNAL( error( const QString& ) ),
00353 this, SLOT( slotLDAPError( const QString& ) ) );
00354
00355 mClients.append( ldapClient );
00356 }
00357 }
00358
00359 connect( &mDataTimer, SIGNAL( timeout() ), SLOT( slotDataTimer() ) );
00360 }
00361
00362 void LdapSearch::startSearch( const QString& txt )
00363 {
00364 if ( mNoLDAPLookup )
00365 return;
00366
00367 cancelSearch();
00368
00369 int pos = txt.find( '\"' );
00370 if( pos >= 0 )
00371 {
00372 ++pos;
00373 int pos2 = txt.find( '\"', pos );
00374 if( pos2 >= 0 )
00375 mSearchText = txt.mid( pos , pos2 - pos );
00376 else
00377 mSearchText = txt.mid( pos );
00378 } else
00379 mSearchText = txt;
00380
00381 QString filter = QString( "|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" )
00382 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText );
00383
00384 QValueList< LdapClient* >::Iterator it;
00385 for ( it = mClients.begin(); it != mClients.end(); ++it ) {
00386 (*it)->startQuery( filter );
00387 ++mActiveClients;
00388 }
00389 }
00390
00391 void LdapSearch::cancelSearch()
00392 {
00393 QValueList< LdapClient* >::Iterator it;
00394 for ( it = mClients.begin(); it != mClients.end(); ++it )
00395 (*it)->cancelQuery();
00396
00397 mActiveClients = 0;
00398 mResults.clear();
00399 }
00400
00401 void LdapSearch::slotLDAPResult( const KABC::LdapObject& obj )
00402 {
00403 mResults.append( obj );
00404 if ( !mDataTimer.isActive() )
00405 mDataTimer.start( 500, true );
00406 }
00407
00408 void LdapSearch::slotLDAPError( const QString& )
00409 {
00410 slotLDAPDone();
00411 }
00412
00413 void LdapSearch::slotLDAPDone()
00414 {
00415 if ( --mActiveClients > 0 )
00416 return;
00417
00418 finish();
00419 }
00420
00421 void LdapSearch::slotDataTimer()
00422 {
00423 emit searchData( makeSearchData() );
00424 }
00425
00426 void LdapSearch::finish()
00427 {
00428 mDataTimer.stop();
00429
00430 emit searchData( makeSearchData() );
00431 emit searchDone();
00432 }
00433
00434 QStringList LdapSearch::makeSearchData()
00435 {
00436 QStringList ret;
00437 QString search_text_upper = mSearchText.upper();
00438
00439 QValueList< KABC::LdapObject >::ConstIterator it1;
00440 for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) {
00441 QString name, mail, givenname, sn;
00442
00443 LdapAttrMap::ConstIterator it2;
00444 for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) {
00445 QString tmp = QString::fromUtf8((*it2).first());
00446 if ( it2.key() == "cn" )
00447 name = tmp;
00448 else if( it2.key() == "mail" )
00449 mail = tmp;
00450 else if( it2.key() == "givenName" )
00451 givenname = tmp;
00452 else if( it2.key() == "sn" )
00453 sn = tmp;
00454 }
00455
00456 if( mail.isEmpty())
00457 ;
00458 else if ( name.isEmpty() )
00459 ret.append( mail );
00460 else {
00461 kdDebug(5700) << "<" << name << "><" << mail << ">" << endl;
00462 ret.append( QString( "%1 <%2>" ).arg( name ).arg( mail ) );
00463 #if 0
00464
00465 if ( givenname.upper().startsWith( search_text_upper ) )
00466 ret.append( QString( "$$%1$%2 <%3>" ).arg( givenname ).arg( name ).arg( mail ) );
00467 if ( sn.upper().startsWith( search_text_upper ) )
00468 ret.append( QString( "$$%1$%2 <%3>" ).arg( sn ).arg( name ).arg( mail ) );
00469 #endif
00470 }
00471 }
00472
00473 mResults.clear();
00474
00475 return ret;
00476 }
00477
00478 bool LdapSearch::isAvailable() const
00479 {
00480 return !mNoLDAPLookup;
00481 }
00482
00483
00484
00485 #include "ldapclient.moc"
00486