00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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 <kdirwatch.h>
00036
#include <kmdcodec.h>
00037
#include <kprotocolinfo.h>
00038
#include <kstandarddirs.h>
00039
00040
#include "ldapclient.h"
00041
#include "ldif.h"
00042
#include "ldapurl.h"
00043
00044
using namespace KPIM;
00045
00046
class LdapClient::LdapClientPrivate{
00047
public:
00048
QString bindDN;
00049
QString pwdBindDN;
00050
LDIF ldif;
00051
int clientNumber;
00052
int completionWeight;
00053 };
00054
00055
QString LdapObject::toString()
const
00056
{
00057
QString result = QString::fromLatin1(
"\ndn: %1\n" ).arg( dn );
00058
for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) {
00059
QString attr = it.key();
00060
for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) {
00061 result += QString::fromUtf8( LDIF::assembleLine( attr, *it2, 76 ) ) +
"\n";
00062 }
00063 }
00064
00065
return result;
00066 }
00067
00068
void LdapObject::clear()
00069 {
00070 dn = QString::null;
00071 objectClass = 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 client = that.
client;
00081 }
00082 }
00083
00084 LdapClient::LdapClient(
int clientNumber,
QObject* parent,
const char* name )
00085 :
QObject( parent, name ), mJob( 0 ), mActive( false ), mReportObjectClass( false )
00086 {
00087 d =
new LdapClientPrivate;
00088 d->clientNumber = clientNumber;
00089 d->completionWeight = 50 - clientNumber;
00090 }
00091
00092 LdapClient::~LdapClient()
00093 {
00094
cancelQuery();
00095
delete d; d = 0;
00096 }
00097
00098
void LdapClient::setHost(
const QString& host )
00099 {
00100 mHost = host;
00101 }
00102
00103
void LdapClient::setPort(
const QString& port )
00104 {
00105 mPort = port;
00106 }
00107
00108
void LdapClient::setBase(
const QString& base )
00109 {
00110 mBase = base;
00111 }
00112
00113
void LdapClient::setBindDN(
const QString& bindDN )
00114 {
00115 d->bindDN = bindDN;
00116 }
00117
00118
void LdapClient::setPwdBindDN(
const QString& pwdBindDN )
00119 {
00120 d->pwdBindDN = pwdBindDN;
00121 }
00122
00123
void LdapClient::setAttrs(
const QStringList& attrs )
00124 {
00125 mAttrs = attrs;
00126
for ( QStringList::Iterator it = mAttrs.begin(); it != mAttrs.end(); ++it )
00127
if( (*it).lower() ==
"objectclass" ){
00128 mReportObjectClass =
true;
00129
return;
00130 }
00131 mAttrs <<
"objectClass";
00132 mReportObjectClass =
false;
00133 }
00134
00135
void LdapClient::startQuery(
const QString& filter )
00136 {
00137
cancelQuery();
00138
LDAPUrl url;
00139
00140 url.setProtocol(
"ldap" );
00141 url.setUser( d->bindDN );
00142 url.setPass( d->pwdBindDN );
00143 url.setHost( mHost );
00144 url.setPort( mPort.toUInt() );
00145 url.
setDn( mBase );
00146 url.
setAttributes( mAttrs );
00147 url.
setScope( mScope ==
"one" ? LDAPUrl::One : LDAPUrl::Sub );
00148 url.
setFilter(
"("+filter+
")" );
00149
00150 kdDebug(5300) <<
"LdapClient: Doing query: " << url.prettyURL() << endl;
00151
00152 startParseLDIF();
00153 mActive =
true;
00154 mJob = KIO::get( url,
false,
false );
00155 connect( mJob, SIGNAL( data( KIO::Job*,
const QByteArray& ) ),
00156
this, SLOT( slotData( KIO::Job*,
const QByteArray& ) ) );
00157 connect( mJob, SIGNAL( infoMessage( KIO::Job*,
const QString& ) ),
00158
this, SLOT( slotInfoMessage( KIO::Job*,
const QString& ) ) );
00159 connect( mJob, SIGNAL(
result( KIO::Job* ) ),
00160
this, SLOT( slotDone() ) );
00161 }
00162
00163
void LdapClient::cancelQuery()
00164 {
00165
if ( mJob ) {
00166 mJob->kill();
00167 mJob = 0;
00168 }
00169
00170 mActive =
false;
00171 }
00172
00173
void LdapClient::slotData( KIO::Job*,
const QByteArray& data )
00174 {
00175
#ifndef NDEBUG // don't create the QString
00176
00177
00178
#endif
00179
parseLDIF( data );
00180 }
00181
00182
void LdapClient::slotInfoMessage( KIO::Job*,
const QString & )
00183 {
00184
00185 }
00186
00187
void LdapClient::slotDone()
00188 {
00189 endParseLDIF();
00190 mActive =
false;
00191
#if 0
00192
for (
QValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) {
00193 qDebug( (*it).toString().latin1() );
00194 }
00195
#endif
00196
int err = mJob->error();
00197
if ( err && err != KIO::ERR_USER_CANCELED ) {
00198 emit
error( KIO::buildErrorString( err,
QString(
"%1:%2").arg( mHost ).arg( mPort ) ) );
00199 }
00200 emit
done();
00201 }
00202
00203
void LdapClient::startParseLDIF()
00204 {
00205 mCurrentObject.
clear();
00206 mLastAttrName = 0;
00207 mLastAttrValue = 0;
00208 mIsBase64 =
false;
00209 d->ldif.startParsing();
00210 }
00211
00212
void LdapClient::endParseLDIF()
00213 {
00214 }
00215
00216
void LdapClient::finishCurrentObject()
00217 {
00218 mCurrentObject.
dn = d->ldif.dn();
00219
if( mCurrentObject.
objectClass.lower() ==
"groupofnames" ){
00220 LdapAttrMap::ConstIterator it = mCurrentObject.
attrs.find(
"mail");
00221
if( it == mCurrentObject.
attrs.end() ){
00222
00223
00224
QString sMail;
00225
QStringList lMail = QStringList::split(
",dc=", mCurrentObject.
dn);
00226
const int n = lMail.count();
00227
if( n ){
00228
if( lMail.first().lower().startsWith(
"cn=") ){
00229 sMail = lMail.first().simplifyWhiteSpace().mid(3);
00230
if( 1 < n )
00231 sMail.append(
'@');
00232
for(
int i=1; i<n; ++i){
00233 sMail.append( lMail[i] );
00234
if( i < n-1 )
00235 sMail.append(
'.');
00236 }
00237 mCurrentObject.
attrs[
"mail"].append( sMail.utf8() );
00238 }
00239 }
00240 }
00241 }
00242 mCurrentObject.
client =
this;
00243 emit
result( mCurrentObject );
00244 mCurrentObject.
clear();
00245 }
00246
00247
void LdapClient::parseLDIF(
const QByteArray& data )
00248 {
00249
00250
if ( data.size() ) {
00251 d->ldif.setLDIF( data );
00252 }
else {
00253 d->ldif.endLDIF();
00254 }
00255
00256 LDIF::ParseVal ret;
00257
QString name;
00258
QByteArray value;
00259
do {
00260 ret = d->ldif.nextItem();
00261
switch ( ret ) {
00262
case LDIF::Item:
00263 {
00264 name = d->ldif.attr();
00265 value = d->ldif.val();
00266
bool bFoundOC = name.lower() ==
"objectclass";
00267
if( bFoundOC )
00268 mCurrentObject.
objectClass = QString::fromUtf8( value, value.size() );
00269
if( mReportObjectClass || !bFoundOC )
00270 mCurrentObject.
attrs[ name ].append( value );
00271
00272 }
00273
break;
00274
case LDIF::EndEntry:
00275 finishCurrentObject();
00276
break;
00277
default:
00278
break;
00279 }
00280 }
while ( ret != LDIF::MoreData );
00281 }
00282
00283
QString LdapClient::bindDN()
const
00284
{
00285
return d->bindDN;
00286 }
00287
00288
QString LdapClient::pwdBindDN()
const
00289
{
00290
return d->pwdBindDN;
00291 }
00292
00293
int LdapClient::clientNumber()
const
00294
{
00295
return d->clientNumber;
00296 }
00297
00298
int LdapClient::completionWeight()
const
00299
{
00300
return d->completionWeight;
00301 }
00302
00303
void KPIM::LdapClient::setCompletionWeight(
int weight )
00304 {
00305 d->completionWeight = weight;
00306 }
00307
00308 LdapSearch::LdapSearch()
00309 : mActiveClients( 0 ), mNoLDAPLookup( false )
00310 {
00311
if ( !KProtocolInfo::isKnownProtocol( KURL(
"ldap://localhost") ) ) {
00312 mNoLDAPLookup =
true;
00313
return;
00314 }
00315
00316 readConfig();
00317 connect(KDirWatch::self(), SIGNAL(dirty (
const QString&)),
this,
00318 SLOT(slotFileChanged(
const QString&)));
00319 }
00320
00321
void LdapSearch::readConfig()
00322 {
00323 cancelSearch();
00324
QValueList< LdapClient* >::Iterator it;
00325
for ( it = mClients.begin(); it != mClients.end(); ++it )
00326
delete *it;
00327 mClients.clear();
00328
00329
00330 KConfig config(
"kabldaprc",
true );
00331 config.setGroup(
"LDAP" );
00332
int numHosts = config.readUnsignedNumEntry(
"NumSelectedHosts");
00333
if ( !numHosts ) {
00334 mNoLDAPLookup =
true;
00335 }
else {
00336
for (
int j = 0; j < numHosts; j++ ) {
00337
LdapClient* ldapClient =
new LdapClient( j,
this );
00338
00339 QString host = config.readEntry( QString(
"SelectedHost%1" ).arg( j ),
"" ).stripWhiteSpace();
00340
if ( !host.isEmpty() )
00341 ldapClient->
setHost( host );
00342
00343 QString port = QString::number( config.readUnsignedNumEntry( QString(
"SelectedPort%1" ).arg( j ) ) );
00344
if ( !port.isEmpty() )
00345 ldapClient->
setPort( port );
00346
00347 QString base = config.readEntry( QString(
"SelectedBase%1" ).arg( j ),
"" ).stripWhiteSpace();
00348
if ( !base.isEmpty() )
00349 ldapClient->
setBase( base );
00350
00351 QString bindDN = config.readEntry( QString(
"SelectedBind%1" ).arg( j ) ).stripWhiteSpace();
00352
if ( !bindDN.isEmpty() )
00353 ldapClient->
setBindDN( bindDN );
00354
00355 QString pwdBindDN = config.readEntry( QString(
"SelectedPwdBind%1" ).arg( j ) ).stripWhiteSpace();
00356
if ( !pwdBindDN.isEmpty() )
00357 ldapClient->
setPwdBindDN( pwdBindDN );
00358
00359
int completionWeight = config.readNumEntry( QString(
"SelectedCompletionWeight%1" ).arg( j ), -1 );
00360
if ( completionWeight != -1 )
00361 ldapClient->
setCompletionWeight( completionWeight );
00362
00363
QStringList attrs;
00364
00365 attrs <<
"cn" <<
"mail" <<
"givenname" <<
"sn" <<
"objectClass";
00366 ldapClient->
setAttrs( attrs );
00367
00368 connect( ldapClient, SIGNAL( result(
const KPIM::LdapObject& ) ),
00369
this, SLOT( slotLDAPResult(
const KPIM::LdapObject& ) ) );
00370 connect( ldapClient, SIGNAL( done() ),
00371
this, SLOT( slotLDAPDone() ) );
00372 connect( ldapClient, SIGNAL( error(
const QString& ) ),
00373
this, SLOT( slotLDAPError(
const QString& ) ) );
00374
00375 mClients.append( ldapClient );
00376 }
00377
00378 connect( &mDataTimer, SIGNAL( timeout() ), SLOT( slotDataTimer() ) );
00379 }
00380 mConfigFile = locateLocal(
"config",
"kabldaprc" );
00381 KDirWatch::self()->addFile( mConfigFile );
00382 }
00383
00384
void LdapSearch::slotFileChanged(
const QString& file )
00385 {
00386
if ( file == mConfigFile )
00387 readConfig();
00388 }
00389
00390
void LdapSearch::startSearch(
const QString& txt )
00391 {
00392
if ( mNoLDAPLookup )
00393
return;
00394
00395 cancelSearch();
00396
00397
int pos = txt.find(
'\"' );
00398
if( pos >= 0 )
00399 {
00400 ++pos;
00401
int pos2 = txt.find(
'\"', pos );
00402
if( pos2 >= 0 )
00403 mSearchText = txt.mid( pos , pos2 - pos );
00404
else
00405 mSearchText = txt.mid( pos );
00406 }
else
00407 mSearchText = txt;
00408
00409 QString filter = QString(
"|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" )
00410 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText );
00411
00412
QValueList< LdapClient* >::Iterator it;
00413
for ( it = mClients.begin(); it != mClients.end(); ++it ) {
00414 (*it)->startQuery( filter );
00415 kdDebug(5300) <<
"LdapSearch::startSearch() " << filter << endl;
00416 ++mActiveClients;
00417 }
00418 }
00419
00420
void LdapSearch::cancelSearch()
00421 {
00422
QValueList< LdapClient* >::Iterator it;
00423
for ( it = mClients.begin(); it != mClients.end(); ++it )
00424 (*it)->cancelQuery();
00425
00426 mActiveClients = 0;
00427 mResults.clear();
00428 }
00429
00430
void LdapSearch::slotLDAPResult(
const KPIM::LdapObject& obj )
00431 {
00432 mResults.append( obj );
00433
if ( !mDataTimer.isActive() )
00434 mDataTimer.start( 500,
true );
00435 }
00436
00437
void LdapSearch::slotLDAPError(
const QString& )
00438 {
00439 slotLDAPDone();
00440 }
00441
00442
void LdapSearch::slotLDAPDone()
00443 {
00444
if ( --mActiveClients > 0 )
00445
return;
00446
00447 finish();
00448 }
00449
00450
void LdapSearch::slotDataTimer()
00451 {
00452
QStringList lst;
00453 LdapResultList reslist;
00454 makeSearchData( lst, reslist );
00455
if ( !lst.isEmpty() )
00456 emit
searchData( lst );
00457
if ( !reslist.isEmpty() )
00458 emit
searchData( reslist );
00459 }
00460
00461
void LdapSearch::finish()
00462 {
00463 mDataTimer.stop();
00464
00465 slotDataTimer();
00466 emit searchDone();
00467 }
00468
00469
void LdapSearch::makeSearchData(
QStringList& ret, LdapResultList& resList )
00470 {
00471 QString search_text_upper = mSearchText.upper();
00472
00473
QValueList< KPIM::LdapObject >::ConstIterator it1;
00474
for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) {
00475 QString name, mail, givenname, sn;
00476
QStringList mails;
00477
bool isDistributionList =
false;
00478
bool wasCN =
false;
00479
bool wasDC =
false;
00480
00481 kdDebug(5300) <<
"\n\nLdapSearch::makeSearchData()\n\n" << endl;
00482
00483 LdapAttrMap::ConstIterator it2;
00484
for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) {
00485
QByteArray val = (*it2).first();
00486
int len = val.size();
00487
if(
'\0' == val[len-1] )
00488 --len;
00489
const QString tmp = QString::fromUtf8( val, len );
00490 kdDebug(5300) <<
" key: \"" << it2.key() <<
"\" value: \"" << tmp <<
"\"" << endl;
00491
if ( it2.key() ==
"cn" ) {
00492 name = tmp;
00493
if( mail.isEmpty() )
00494 mail = tmp;
00495
else{
00496
if( wasCN )
00497 mail.prepend(
"." );
00498
else
00499 mail.prepend(
"@" );
00500 mail.prepend( tmp );
00501 }
00502 wasCN =
true;
00503 }
else if ( it2.key() ==
"dc" ) {
00504
if( mail.isEmpty() )
00505 mail = tmp;
00506
else{
00507
if( wasDC )
00508 mail.append(
"." );
00509
else
00510 mail.append(
"@" );
00511 mail.append( tmp );
00512 }
00513 wasDC =
true;
00514 }
else if( it2.key() ==
"mail" ) {
00515 mail = tmp;
00516 LdapAttrValue::ConstIterator it3 = it2.data().begin();
00517
for ( ; it3 != it2.data().end(); ++it3 ) {
00518 mails.append( QString::fromUtf8( (*it3).data(), (*it3).size() ) );
00519 }
00520 }
else if( it2.key() ==
"givenName" )
00521 givenname = tmp;
00522
else if( it2.key() ==
"sn" )
00523 sn = tmp;
00524
else if( it2.key() ==
"objectClass" && tmp ==
"groupOfNames" ) {
00525 isDistributionList =
true;
00526 }
00527 }
00528
00529
if( mails.isEmpty()) {
00530
if ( !mail.isEmpty() ) mails.append( mail );
00531
if( isDistributionList ) {
00532 kdDebug(5300) <<
"\n\nLdapSearch::makeSearchData() found a list: " << name <<
"\n\n" << endl;
00533 ret.append( name );
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548 }
else {
00549 kdDebug(5300) <<
"LdapSearch::makeSearchData() found BAD ENTRY: \"" << name <<
"\"" << endl;
00550
continue;
00551 }
00552 }
else if ( name.isEmpty() ) {
00553 kdDebug(5300) <<
"LdapSearch::makeSearchData() mail: \"" << mail <<
"\"" << endl;
00554 ret.append( mail );
00555 }
else {
00556 kdDebug(5300) <<
"LdapSearch::makeSearchData() name: \"" << name <<
"\" mail: \"" << mail <<
"\"" << endl;
00557 ret.append( QString(
"%1 <%2>" ).arg( name ).arg( mail ) );
00558 }
00559
00560 LdapResult sr;
00561 sr.clientNumber = (*it1).client->clientNumber();
00562 sr.completionWeight = (*it1).client->completionWeight();
00563 sr.name = name;
00564 sr.email = mails;
00565 resList.append( sr );
00566 }
00567
00568 mResults.clear();
00569 }
00570
00571
bool LdapSearch::isAvailable()
const
00572
{
00573
return !mNoLDAPLookup;
00574 }
00575
00576
#include "ldapclient.moc"