kabc Library API Documentation

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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
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         //l->setPixmap( QPixmap("tmp.jpg") );
00058         //l->show();
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   //qDebug("Job said \"%s\"", info.latin1());
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         //qDebug("_lastAttrValue=\"%s\", output length %d", _lastAttrValue.data(), out.size());
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   //qDebug("%s", data.data());
00213   if ( data.isNull() || data.isEmpty() )
00214     return;
00215   mBuf += QCString( data, data.size() + 1 ); // collect data in buffer
00216   int nl;
00217   while ( (nl = mBuf.find('\n')) != -1 ) {
00218     // Run through it line by line
00219     /* FIXME(steffen): This could be a problem
00220     * with "no newline at end of file" input
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 ] == '#' ) { // comment
00230         continue;
00231       } else if ( line[ 0 ] == ' ' || line[ 0 ] == '\t' ) { // continuation of last line
00232         line = line.stripWhiteSpace();
00233         //qDebug("Adding \"%s\"", line.data() );
00234         mLastAttrValue += line;
00235         continue;
00236       }
00237     } else
00238       continue;
00239 
00240     int colon = line.find(':');
00241     if ( colon != -1 ) { // Found new attribute 
00242       if ( mLastAttrName == "dn" ) { // New object, store the current
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         // Store current value, take care of decoding
00252         if ( mIsBase64 ) {
00253           QByteArray out;
00254           KCodecs::base64Decode( mLastAttrValue, out );
00255           //qDebug("_lastAttrValue=\"%s\", output length %d", _lastAttrValue.data(), out.size());
00256           mCurrentObject.attrs[ mLastAttrName ].append( out );
00257         } else {
00258           mCurrentObject.attrs[ mLastAttrName ].append( mLastAttrValue );
00259         }
00260       }
00261 
00262       mLastAttrName  = line.left( colon ).stripWhiteSpace();
00263       //qDebug("Found attr %s", _lastAttrName.data() );
00264       ++colon;
00265       if ( line[colon] == ':' ) {
00266         mIsBase64 = true;
00267         //qDebug("BASE64");
00268         ++colon;
00269       } else {
00270         //qDebug("UTF8");
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   // stolen from KAddressBook
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() ); // TODO loop?
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         ; // nothing, bad entry
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       // this sucks
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"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:16:07 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001