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