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
const QString sClass( mCurrentObject.
objectClass.lower() );
00220
if( sClass ==
"groupofnames" || sClass ==
"kolabgroupofnames" ){
00221 LdapAttrMap::ConstIterator it = mCurrentObject.
attrs.find(
"mail");
00222
if( it == mCurrentObject.
attrs.end() ){
00223
00224
00225
QString sMail;
00226
QStringList lMail = QStringList::split(
",dc=", mCurrentObject.
dn);
00227
const int n = lMail.count();
00228
if( n ){
00229
if( lMail.first().lower().startsWith(
"cn=") ){
00230 sMail = lMail.first().simplifyWhiteSpace().mid(3);
00231
if( 1 < n )
00232 sMail.append(
'@');
00233
for(
int i=1; i<n; ++i){
00234 sMail.append( lMail[i] );
00235
if( i < n-1 )
00236 sMail.append(
'.');
00237 }
00238 mCurrentObject.
attrs[
"mail"].append( sMail.utf8() );
00239 }
00240 }
00241 }
00242 }
00243 mCurrentObject.
client =
this;
00244 emit result( mCurrentObject );
00245 mCurrentObject.
clear();
00246 }
00247
00248
void LdapClient::parseLDIF(
const QByteArray& data )
00249 {
00250
00251
if ( data.size() ) {
00252 d->ldif.setLDIF( data );
00253 }
else {
00254 d->ldif.endLDIF();
00255 }
00256
00257 LDIF::ParseVal ret;
00258
QString name;
00259
QByteArray value;
00260
do {
00261 ret = d->ldif.nextItem();
00262
switch ( ret ) {
00263
case LDIF::Item:
00264 {
00265 name = d->ldif.attr();
00266 value = d->ldif.val();
00267
bool bFoundOC = name.lower() ==
"objectclass";
00268
if( bFoundOC )
00269 mCurrentObject.
objectClass = QString::fromUtf8( value, value.size() );
00270
if( mReportObjectClass || !bFoundOC )
00271 mCurrentObject.
attrs[ name ].append( value );
00272
00273 }
00274
break;
00275
case LDIF::EndEntry:
00276 finishCurrentObject();
00277
break;
00278
default:
00279
break;
00280 }
00281 }
while ( ret != LDIF::MoreData );
00282 }
00283
00284
QString LdapClient::bindDN()
const
00285
{
00286
return d->bindDN;
00287 }
00288
00289
QString LdapClient::pwdBindDN()
const
00290
{
00291
return d->pwdBindDN;
00292 }
00293
00294
int LdapClient::clientNumber()
const
00295
{
00296
return d->clientNumber;
00297 }
00298
00299
int LdapClient::completionWeight()
const
00300
{
00301
return d->completionWeight;
00302 }
00303
00304
void KPIM::LdapClient::setCompletionWeight(
int weight )
00305 {
00306 d->completionWeight = weight;
00307 }
00308
00309 LdapSearch::LdapSearch()
00310 : mActiveClients( 0 ), mNoLDAPLookup( false )
00311 {
00312
if ( !KProtocolInfo::isKnownProtocol( KURL(
"ldap://localhost") ) ) {
00313 mNoLDAPLookup =
true;
00314
return;
00315 }
00316
00317 readConfig();
00318 connect(KDirWatch::self(), SIGNAL(dirty (
const QString&)),
this,
00319 SLOT(slotFileChanged(
const QString&)));
00320 }
00321
00322
void LdapSearch::readConfig()
00323 {
00324
00325 cancelSearch();
00326
QValueList< LdapClient* >::Iterator it;
00327
for ( it = mClients.begin(); it != mClients.end(); ++it )
00328
delete *it;
00329 mClients.clear();
00330
00331
00332 KConfig config(
"kabldaprc",
true );
00333 config.setGroup(
"LDAP" );
00334
int numHosts = config.readUnsignedNumEntry(
"NumSelectedHosts");
00335
if ( !numHosts ) {
00336 mNoLDAPLookup =
true;
00337 }
else {
00338
for (
int j = 0; j < numHosts; j++ ) {
00339
LdapClient* ldapClient =
new LdapClient( j,
this );
00340
00341 QString host = config.readEntry( QString(
"SelectedHost%1" ).arg( j ),
"" ).stripWhiteSpace();
00342
if ( !host.isEmpty() ){
00343 ldapClient->
setHost( host );
00344 mNoLDAPLookup =
false;
00345 }
00346
00347 QString port = QString::number( config.readUnsignedNumEntry( QString(
"SelectedPort%1" ).arg( j ) ) );
00348
if ( !port.isEmpty() )
00349 ldapClient->
setPort( port );
00350
00351 QString base = config.readEntry( QString(
"SelectedBase%1" ).arg( j ),
"" ).stripWhiteSpace();
00352
if ( !base.isEmpty() )
00353 ldapClient->
setBase( base );
00354
00355 QString bindDN = config.readEntry( QString(
"SelectedBind%1" ).arg( j ) ).stripWhiteSpace();
00356
if ( !bindDN.isEmpty() )
00357 ldapClient->
setBindDN( bindDN );
00358
00359 QString pwdBindDN = config.readEntry( QString(
"SelectedPwdBind%1" ).arg( j ) ).stripWhiteSpace();
00360
if ( !pwdBindDN.isEmpty() )
00361 ldapClient->
setPwdBindDN( pwdBindDN );
00362
00363
int completionWeight = config.readNumEntry( QString(
"SelectedCompletionWeight%1" ).arg( j ), -1 );
00364
if ( completionWeight != -1 )
00365 ldapClient->
setCompletionWeight( completionWeight );
00366
00367
QStringList attrs;
00368
00369 attrs <<
"cn" <<
"mail" <<
"givenname" <<
"sn" <<
"objectClass";
00370 ldapClient->
setAttrs( attrs );
00371
00372 connect( ldapClient, SIGNAL( result(
const KPIM::LdapObject& ) ),
00373
this, SLOT( slotLDAPResult(
const KPIM::LdapObject& ) ) );
00374 connect( ldapClient, SIGNAL( done() ),
00375
this, SLOT( slotLDAPDone() ) );
00376 connect( ldapClient, SIGNAL( error(
const QString& ) ),
00377
this, SLOT( slotLDAPError(
const QString& ) ) );
00378
00379 mClients.append( ldapClient );
00380 }
00381
00382 connect( &mDataTimer, SIGNAL( timeout() ), SLOT( slotDataTimer() ) );
00383 }
00384 mConfigFile = locateLocal(
"config",
"kabldaprc" );
00385 KDirWatch::self()->addFile( mConfigFile );
00386 }
00387
00388
void LdapSearch::slotFileChanged(
const QString& file )
00389 {
00390
00391
if ( file == mConfigFile )
00392 readConfig();
00393 }
00394
00395
void LdapSearch::startSearch(
const QString& txt )
00396 {
00397
if ( mNoLDAPLookup )
00398
return;
00399
00400 cancelSearch();
00401
00402
int pos = txt.find(
'\"' );
00403
if( pos >= 0 )
00404 {
00405 ++pos;
00406
int pos2 = txt.find(
'\"', pos );
00407
if( pos2 >= 0 )
00408 mSearchText = txt.mid( pos , pos2 - pos );
00409
else
00410 mSearchText = txt.mid( pos );
00411 }
else
00412 mSearchText = txt;
00413
00414 QString filter = QString(
"|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" )
00415 .arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText );
00416
00417
QValueList< LdapClient* >::Iterator it;
00418
for ( it = mClients.begin(); it != mClients.end(); ++it ) {
00419 (*it)->startQuery( filter );
00420 kdDebug(5300) <<
"LdapSearch::startSearch() " << filter << endl;
00421 ++mActiveClients;
00422 }
00423 }
00424
00425
void LdapSearch::cancelSearch()
00426 {
00427
QValueList< LdapClient* >::Iterator it;
00428
for ( it = mClients.begin(); it != mClients.end(); ++it )
00429 (*it)->cancelQuery();
00430
00431 mActiveClients = 0;
00432 mResults.clear();
00433 }
00434
00435
void LdapSearch::slotLDAPResult(
const KPIM::LdapObject& obj )
00436 {
00437 mResults.append( obj );
00438
if ( !mDataTimer.isActive() )
00439 mDataTimer.start( 500,
true );
00440 }
00441
00442
void LdapSearch::slotLDAPError(
const QString& )
00443 {
00444 slotLDAPDone();
00445 }
00446
00447
void LdapSearch::slotLDAPDone()
00448 {
00449
if ( --mActiveClients > 0 )
00450
return;
00451
00452 finish();
00453 }
00454
00455
void LdapSearch::slotDataTimer()
00456 {
00457
QStringList lst;
00458 LdapResultList reslist;
00459 makeSearchData( lst, reslist );
00460
if ( !lst.isEmpty() )
00461 emit
searchData( lst );
00462
if ( !reslist.isEmpty() )
00463 emit searchData( reslist );
00464 }
00465
00466
void LdapSearch::finish()
00467 {
00468 mDataTimer.stop();
00469
00470 slotDataTimer();
00471 emit searchDone();
00472 }
00473
00474
void LdapSearch::makeSearchData(
QStringList& ret, LdapResultList& resList )
00475 {
00476 QString search_text_upper = mSearchText.upper();
00477
00478
QValueList< KPIM::LdapObject >::ConstIterator it1;
00479
for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) {
00480 QString name, mail, givenname, sn;
00481
QStringList mails;
00482
bool isDistributionList =
false;
00483
bool wasCN =
false;
00484
bool wasDC =
false;
00485
00486 kdDebug(5300) <<
"\n\nLdapSearch::makeSearchData()\n\n" << endl;
00487
00488 LdapAttrMap::ConstIterator it2;
00489
for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) {
00490
QByteArray val = (*it2).first();
00491
int len = val.size();
00492
if( len > 0 &&
'\0' == val[len-1] )
00493 --len;
00494
const QString tmp = QString::fromUtf8( val, len );
00495 kdDebug(5300) <<
" key: \"" << it2.key() <<
"\" value: \"" << tmp <<
"\"" << endl;
00496
if ( it2.key() ==
"cn" ) {
00497 name = tmp;
00498
if( mail.isEmpty() )
00499 mail = tmp;
00500
else{
00501
if( wasCN )
00502 mail.prepend(
"." );
00503
else
00504 mail.prepend(
"@" );
00505 mail.prepend( tmp );
00506 }
00507 wasCN =
true;
00508 }
else if ( it2.key() ==
"dc" ) {
00509
if( mail.isEmpty() )
00510 mail = tmp;
00511
else{
00512
if( wasDC )
00513 mail.append(
"." );
00514
else
00515 mail.append(
"@" );
00516 mail.append( tmp );
00517 }
00518 wasDC =
true;
00519 }
else if( it2.key() ==
"mail" ) {
00520 mail = tmp;
00521 LdapAttrValue::ConstIterator it3 = it2.data().begin();
00522
for ( ; it3 != it2.data().end(); ++it3 ) {
00523 mails.append( QString::fromUtf8( (*it3).data(), (*it3).size() ) );
00524 }
00525 }
else if( it2.key() ==
"givenName" )
00526 givenname = tmp;
00527
else if( it2.key() ==
"sn" )
00528 sn = tmp;
00529
else if( it2.key() ==
"objectClass" &&
00530 ( tmp ==
"groupOfNames" || tmp ==
"kolabGroupOfNames" ) ) {
00531 isDistributionList =
true;
00532 }
00533 }
00534
00535
if( mails.isEmpty()) {
00536
if ( !mail.isEmpty() ) mails.append( mail );
00537
if( isDistributionList ) {
00538 kdDebug(5300) <<
"\n\nLdapSearch::makeSearchData() found a list: " << name <<
"\n\n" << endl;
00539 ret.append( name );
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554 }
else {
00555 kdDebug(5300) <<
"LdapSearch::makeSearchData() found BAD ENTRY: \"" << name <<
"\"" << endl;
00556
continue;
00557 }
00558 }
else if ( name.isEmpty() ) {
00559 kdDebug(5300) <<
"LdapSearch::makeSearchData() mail: \"" << mail <<
"\"" << endl;
00560 ret.append( mail );
00561 }
else {
00562 kdDebug(5300) <<
"LdapSearch::makeSearchData() name: \"" << name <<
"\" mail: \"" << mail <<
"\"" << endl;
00563 ret.append( QString(
"%1 <%2>" ).arg( name ).arg( mail ) );
00564 }
00565
00566 LdapResult sr;
00567 sr.clientNumber = (*it1).client->clientNumber();
00568 sr.completionWeight = (*it1).client->completionWeight();
00569 sr.name = name;
00570 sr.email = mails;
00571 resList.append( sr );
00572 }
00573
00574 mResults.clear();
00575 }
00576
00577
bool LdapSearch::isAvailable()
const
00578
{
00579
return !mNoLDAPLookup;
00580 }
00581
00582
#include "ldapclient.moc"