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 
00024 
00025 #include <qfile.h>
00026 #include <qimage.h>
00027 #include <qlabel.h>
00028 #include <qpixmap.h>
00029 #include <qtextstream.h>
00030 #include <qurl.h>
00031 
00032 #include <kapplication.h>
00033 #include <kconfig.h>
00034 #include <kdebug.h>
00035 #include <kmdcodec.h>
00036 #include <kprotocolinfo.h>
00037 
00038 #include "ldapclient.h"
00039 
00040 using namespace KABC;
00041 
00042 class LdapClient::LdapClientPrivate{
00043 public:
00044   QString bindDN;
00045   QString pwdBindDN;
00046 };
00047 
00048 QString LdapObject::toString() const
00049 {
00050   QString result = QString::fromLatin1( "\ndn: %1\n" ).arg( dn );
00051   for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) {
00052     QString attr = it.key();
00053     for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) {
00054       if ( attr == "jpegPhoto" ) {
00055         QByteArray buf = *it2;
00056 #if 0
00057         qDebug( "Trying to load image from buf with size %d", (*it2).size() );
00058         QPixmap pix;
00059         pix.loadFromData( buf, "JPEG" );
00060         qDebug( "Image loaded successfully" );
00061         QLabel* l = new QLabel( 0 );
00062         QFile f( "tmp.jpg" );
00063         f.open( IO_WriteOnly );
00064         f.writeBlock( buf );
00065         f.close();
00066         //l->setPixmap( QPixmap("tmp.jpg") );
00067         //l->show();
00068 #endif
00069       } else {
00070         result += QString("%1: %2\n").arg(attr).arg(QString::fromUtf8(*it2));
00071       }
00072     }
00073   }
00074 
00075   return result;
00076 }
00077 
00078 void LdapObject::clear()
00079 {
00080   dn = QString::null;
00081   attrs.clear();
00082 }
00083 
00084 void LdapObject::assign( const LdapObject& that )
00085 {
00086   if ( &that != this ) {
00087     dn = that.dn;
00088     attrs = that.attrs;
00089   }
00090 }
00091 
00092 LdapClient::LdapClient( QObject* parent, const char* name )
00093   : QObject( parent, name ), mJob( 0 ), mActive( false )
00094 {
00095   d = new LdapClientPrivate;
00096 }
00097 
00098 LdapClient::~LdapClient()
00099 {
00100   cancelQuery();
00101   delete d; d = 0;
00102 }
00103 
00104 void LdapClient::setHost( const QString& host )
00105 {
00106   mHost = host;
00107 }
00108 
00109 void LdapClient::setPort( const QString& port )
00110 {
00111   mPort = port;
00112 }
00113 
00114 void LdapClient::setBase( const QString& base )
00115 {
00116   mBase = base;
00117 }
00118 
00119 void LdapClient::setBindDN( const QString& bindDN )
00120 {
00121   d->bindDN = bindDN;
00122 }
00123 
00124 void LdapClient::setPwdBindDN( const QString& pwdBindDN )
00125 {
00126   d->pwdBindDN = pwdBindDN;
00127 }
00128 
00129 void LdapClient::setAttrs( const QStringList& attrs )
00130 {
00131   mAttrs = attrs;
00132 }
00133 
00134 void LdapClient::startQuery( const QString& filter )
00135 {
00136   cancelQuery();
00137   QString query;
00138   if ( mScope.isEmpty() )
00139     mScope = "sub";
00140 
00141   QString auth;
00142   QString encodedPassword;
00143   if ( !d->bindDN.isEmpty() ) {
00144     auth = d->bindDN;
00145     QUrl::encode( auth );
00146     if ( !d->pwdBindDN.isEmpty() ) {
00147       encodedPassword = d->pwdBindDN;
00148       QUrl::encode( encodedPassword );
00149       auth += ":" + encodedPassword;
00150     }
00151     auth += "@";
  }

  QString host = mHost;
  if ( !mPort.isEmpty() ) {
    host += ':';
    host += mPort;
  }

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