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