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 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         //l->setPixmap( QPixmap("tmp.jpg") );
00064         //l->show();
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   //qDebug("Job said \"%s\"", info.latin1());
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         //qDebug("_lastAttrValue=\"%s\", output length %d", _lastAttrValue.data(), out.size());
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   // qDebug("%s: %s", __FUNCTION__, data.data());
00229   if ( data.isEmpty() )
00230     return;
00231   mBuf += QCString( data, data.size() + 1 ); // collect data in buffer
00232   int nl;
00233   while ( (nl = mBuf.find('\n')) != -1 ) {
00234     // Run through it line by line
00235     /* FIXME(steffen): This could be a problem
00236     * with "no newline at end of file" input
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 ] == '#' ) { // comment
00246         continue;
00247       } else if ( line[ 0 ] == ' ' || line[ 0 ] == '\t' ) { // continuation of last line
00248         line = line.stripWhiteSpace();
00249         //qDebug("Adding \"%s\"", line.data() );
00250         mLastAttrValue += line;
00251         continue;
00252       }
00253     } else
00254       continue;
00255 
00256     int colon = line.find(':');
00257     if ( colon != -1 ) { // Found new attribute
00258       if ( mLastAttrName == "dn" ) { // New object, store the current
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         // Store current value, take care of decoding
00268         if ( mIsBase64 ) {
00269           QByteArray out;
00270           KCodecs::base64Decode( mLastAttrValue, out );
00271           //qDebug("_lastAttrValue=\"%s\", output length %d", _lastAttrValue.data(), out.size());
00272           mCurrentObject.attrs[ mLastAttrName ].append( out );
00273         } else {
00274           mCurrentObject.attrs[ mLastAttrName ].append( mLastAttrValue );
00275         }
00276       }
00277 
00278       mLastAttrName  = line.left( colon ).stripWhiteSpace();
00279       //qDebug("Found attr %s", _lastAttrName.data() );
00280       ++colon;
00281       if ( line[colon] == ':' ) {
00282         mIsBase64 = true;
00283         //qDebug("BASE64");
00284         ++colon;
00285       } else {
00286         //qDebug("UTF8");
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   // stolen from KAddressBook
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; // TODO loop?
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         ; // nothing, bad entry
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       // this sucks
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 
KDE Logo
This file is part of the documentation for kabc Library Version 3.2.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Feb 5 13:38:41 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2003