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