mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 02:42:51 +00:00
424 lines
12 KiB
C++
424 lines
12 KiB
C++
![]() |
/* kldapclient.cpp - LDAP access
|
||
|
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
|
||
|
*
|
||
|
* Author: Steffen Hansen <hansen@kde.org>
|
||
|
*
|
||
|
* Ported to KABC by Daniel Molkentin <molkentin@kde.org>
|
||
|
*
|
||
|
* Copyright (C) 2013 Laurent Montel <montel@kde.org>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public License
|
||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "ldapclientsearch.h"
|
||
|
#include "ldapclientsearchconfig.h"
|
||
|
|
||
|
#include "ldapclient.h"
|
||
|
#include "ldapsession.h"
|
||
|
#include "ldapqueryjob.h"
|
||
|
|
||
|
#include <kldap/ldapserver.h>
|
||
|
#include <kldap/ldapurl.h>
|
||
|
#include <kldap/ldif.h>
|
||
|
|
||
|
#include <KConfig>
|
||
|
#include <KConfigGroup>
|
||
|
#include <KDebug>
|
||
|
#include <KDirWatch>
|
||
|
#include <KProtocolInfo>
|
||
|
#include <KStandardDirs>
|
||
|
#include <kio/job.h>
|
||
|
|
||
|
#include <QtCore/QPointer>
|
||
|
#include <QtCore/QTimer>
|
||
|
|
||
|
|
||
|
using namespace KLDAP;
|
||
|
|
||
|
class LdapClientSearch::Private
|
||
|
{
|
||
|
public:
|
||
|
Private( LdapClientSearch *qq )
|
||
|
: q( qq ),
|
||
|
mActiveClients( 0 ),
|
||
|
mNoLDAPLookup( false )
|
||
|
{
|
||
|
mClientSearchConfig = new LdapClientSearchConfig;
|
||
|
}
|
||
|
|
||
|
~Private()
|
||
|
{
|
||
|
delete mClientSearchConfig;
|
||
|
}
|
||
|
|
||
|
void readWeighForClient( LdapClient *client, const KConfigGroup &config, int clientNumber );
|
||
|
void readConfig();
|
||
|
void finish();
|
||
|
void makeSearchData( QStringList &ret, LdapResult::List &resList );
|
||
|
|
||
|
void slotLDAPResult( const KLDAP::LdapClient &client, const KLDAP::LdapObject& );
|
||
|
void slotLDAPError( const QString& );
|
||
|
void slotLDAPDone();
|
||
|
void slotDataTimer();
|
||
|
void slotFileChanged( const QString& );
|
||
|
|
||
|
LdapClientSearch *q;
|
||
|
QList<LdapClient*> mClients;
|
||
|
QStringList mAttributes;
|
||
|
QString mSearchText;
|
||
|
QString mFilter;
|
||
|
QTimer mDataTimer;
|
||
|
int mActiveClients;
|
||
|
bool mNoLDAPLookup;
|
||
|
QList<LdapResultObject> mResults;
|
||
|
QString mConfigFile;
|
||
|
LdapClientSearchConfig *mClientSearchConfig;
|
||
|
};
|
||
|
|
||
|
LdapClientSearch::LdapClientSearch( QObject *parent )
|
||
|
: QObject( parent ), d( new Private( this ) )
|
||
|
{
|
||
|
if ( !KProtocolInfo::isKnownProtocol( KUrl( "ldap://localhost" ) ) ) {
|
||
|
d->mNoLDAPLookup = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
d->mAttributes << QLatin1String("cn")
|
||
|
<< QLatin1String("mail")
|
||
|
<< QLatin1String("givenname")
|
||
|
<< QLatin1String("sn");
|
||
|
|
||
|
// Set the filter, to make sure old usage (before 4.14) of this object still works.
|
||
|
d->mFilter = QString::fromLatin1("&(|(objectclass=person)(objectclass=groupOfNames)(mail=*))"
|
||
|
"(|(cn=%1*)(mail=%1*)(givenName=%1*)(sn=%1*))");
|
||
|
|
||
|
d->readConfig();
|
||
|
connect( KDirWatch::self(), SIGNAL(dirty(QString)), this,
|
||
|
SLOT(slotFileChanged(QString)) );
|
||
|
|
||
|
}
|
||
|
|
||
|
LdapClientSearch::~LdapClientSearch()
|
||
|
{
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::readWeighForClient( LdapClient *client, const KConfigGroup &config,
|
||
|
int clientNumber )
|
||
|
{
|
||
|
const int completionWeight = config.readEntry( QString::fromLatin1( "SelectedCompletionWeight%1" ).arg( clientNumber ), -1 );
|
||
|
if ( completionWeight != -1 ) {
|
||
|
client->setCompletionWeight( completionWeight );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::updateCompletionWeights()
|
||
|
{
|
||
|
KConfigGroup config( KLDAP::LdapClientSearchConfig::config(), "LDAP" );
|
||
|
for ( int i = 0; i < d->mClients.size(); ++i ) {
|
||
|
d->readWeighForClient( d->mClients[ i ], config, i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QList<LdapClient*> LdapClientSearch::clients() const
|
||
|
{
|
||
|
return d->mClients;
|
||
|
}
|
||
|
|
||
|
QString LdapClientSearch::filter() const
|
||
|
{
|
||
|
return d->mFilter;
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::setFilter(const QString &filter)
|
||
|
{
|
||
|
d->mFilter = filter;
|
||
|
}
|
||
|
|
||
|
QStringList LdapClientSearch::attributes() const
|
||
|
{
|
||
|
return d->mAttributes;
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::setAttributes(const QStringList &attrs)
|
||
|
{
|
||
|
|
||
|
if ( attrs != d->mAttributes ) {
|
||
|
d->mAttributes = attrs;
|
||
|
d->readConfig();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void LdapClientSearch::Private::readConfig()
|
||
|
{
|
||
|
q->cancelSearch();
|
||
|
qDeleteAll( mClients );
|
||
|
mClients.clear();
|
||
|
|
||
|
// stolen from KAddressBook
|
||
|
KConfigGroup config( KLDAP::LdapClientSearchConfig::config(), "LDAP" );
|
||
|
const int numHosts = config.readEntry( "NumSelectedHosts", 0 );
|
||
|
if ( !numHosts ) {
|
||
|
mNoLDAPLookup = true;
|
||
|
} else {
|
||
|
for ( int j = 0; j < numHosts; ++j ) {
|
||
|
LdapClient *ldapClient = new LdapClient( j, q );
|
||
|
KLDAP::LdapServer server;
|
||
|
mClientSearchConfig->readConfig( server, config, j, true );
|
||
|
if ( !server.host().isEmpty() ) {
|
||
|
mNoLDAPLookup = false;
|
||
|
}
|
||
|
ldapClient->setServer( server );
|
||
|
|
||
|
readWeighForClient( ldapClient, config, j );
|
||
|
|
||
|
ldapClient->setAttributes( mAttributes );
|
||
|
|
||
|
q->connect( ldapClient, SIGNAL(result(KLDAP::LdapClient,KLDAP::LdapObject)),
|
||
|
q, SLOT(slotLDAPResult(KLDAP::LdapClient,KLDAP::LdapObject)) );
|
||
|
q->connect( ldapClient, SIGNAL(done()),
|
||
|
q, SLOT(slotLDAPDone()) );
|
||
|
q->connect( ldapClient, SIGNAL(error(QString)),
|
||
|
q, SLOT(slotLDAPError(QString)) );
|
||
|
|
||
|
mClients.append( ldapClient );
|
||
|
}
|
||
|
|
||
|
q->connect( &mDataTimer, SIGNAL(timeout()), SLOT(slotDataTimer()) );
|
||
|
}
|
||
|
mConfigFile = KStandardDirs::locateLocal( "config", QLatin1String("kabldaprc") );
|
||
|
KDirWatch::self()->addFile( mConfigFile );
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::slotFileChanged( const QString &file )
|
||
|
{
|
||
|
if ( file == mConfigFile ) {
|
||
|
readConfig();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::startSearch( const QString &txt )
|
||
|
{
|
||
|
if ( d->mNoLDAPLookup ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
cancelSearch();
|
||
|
|
||
|
int pos = txt.indexOf( QLatin1Char('\"') );
|
||
|
if ( pos >= 0 ) {
|
||
|
++pos;
|
||
|
const int pos2 = txt.indexOf( QLatin1Char('\"'), pos );
|
||
|
if ( pos2 >= 0 ) {
|
||
|
d->mSearchText = txt.mid( pos, pos2 - pos );
|
||
|
} else {
|
||
|
d->mSearchText = txt.mid( pos );
|
||
|
}
|
||
|
} else {
|
||
|
d->mSearchText = txt;
|
||
|
}
|
||
|
|
||
|
const QString filter = d->mFilter.arg( d->mSearchText );
|
||
|
|
||
|
QList<LdapClient*>::Iterator it;
|
||
|
QList<LdapClient*>::Iterator end(d->mClients.end());
|
||
|
for ( it = d->mClients.begin(); it != end; ++it ) {
|
||
|
(*it)->startQuery( filter );
|
||
|
kDebug(5300) <<"LdapClientSearch::startSearch()" << filter;
|
||
|
++d->mActiveClients;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::cancelSearch()
|
||
|
{
|
||
|
QList<LdapClient*>::Iterator it;
|
||
|
QList<LdapClient*>::Iterator end(d->mClients.end());
|
||
|
for ( it = d->mClients.begin(); it != end; ++it ) {
|
||
|
(*it)->cancelQuery();
|
||
|
}
|
||
|
|
||
|
d->mActiveClients = 0;
|
||
|
d->mResults.clear();
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::slotLDAPResult( const LdapClient &client,
|
||
|
const KLDAP::LdapObject &obj )
|
||
|
{
|
||
|
LdapResultObject result;
|
||
|
result.client = &client;
|
||
|
result.object = obj;
|
||
|
|
||
|
mResults.append( result );
|
||
|
if ( !mDataTimer.isActive() ) {
|
||
|
mDataTimer.setSingleShot( true );
|
||
|
mDataTimer.start( 500 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::slotLDAPError( const QString& )
|
||
|
{
|
||
|
slotLDAPDone();
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::slotLDAPDone()
|
||
|
{
|
||
|
if ( --mActiveClients > 0 ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
finish();
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::slotDataTimer()
|
||
|
{
|
||
|
QStringList lst;
|
||
|
LdapResult::List reslist;
|
||
|
|
||
|
emit q->searchData(mResults);
|
||
|
|
||
|
makeSearchData( lst, reslist );
|
||
|
if ( !lst.isEmpty() ) {
|
||
|
emit q->searchData( lst );
|
||
|
}
|
||
|
if ( !reslist.isEmpty() ) {
|
||
|
emit q->searchData( reslist );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::finish()
|
||
|
{
|
||
|
mDataTimer.stop();
|
||
|
|
||
|
slotDataTimer(); // emit final bunch of data
|
||
|
emit q->searchDone();
|
||
|
}
|
||
|
|
||
|
void LdapClientSearch::Private::makeSearchData( QStringList &ret, LdapResult::List &resList )
|
||
|
{
|
||
|
|
||
|
QList< LdapResultObject >::ConstIterator it1;
|
||
|
QList< LdapResultObject >::ConstIterator end1(mResults.constEnd());
|
||
|
for ( it1 = mResults.constBegin(); it1 != end1; ++it1 ) {
|
||
|
QString name, mail, givenname, sn;
|
||
|
QStringList mails;
|
||
|
bool isDistributionList = false;
|
||
|
bool wasCN = false;
|
||
|
bool wasDC = false;
|
||
|
|
||
|
//kDebug(5300) <<"\n\nLdapClientSearch::makeSearchData()";
|
||
|
|
||
|
KLDAP::LdapAttrMap::ConstIterator it2;
|
||
|
for ( it2 = (*it1).object.attributes().constBegin();
|
||
|
it2 != (*it1).object.attributes().constEnd(); ++it2 ) {
|
||
|
QByteArray val = (*it2).first();
|
||
|
int len = val.size();
|
||
|
if ( len > 0 && '\0' == val[len-1] ) {
|
||
|
--len;
|
||
|
}
|
||
|
const QString tmp = QString::fromUtf8( val, len );
|
||
|
//kDebug(5300) <<" key: \"" << it2.key() <<"\" value: \"" << tmp <<"\"";
|
||
|
if ( it2.key() == QLatin1String("cn") ) {
|
||
|
name = tmp;
|
||
|
if ( mail.isEmpty() ) {
|
||
|
mail = tmp;
|
||
|
} else {
|
||
|
if ( wasCN ) {
|
||
|
mail.prepend( QLatin1String(".") );
|
||
|
} else {
|
||
|
mail.prepend( QLatin1String("@") );
|
||
|
}
|
||
|
mail.prepend( tmp );
|
||
|
}
|
||
|
wasCN = true;
|
||
|
} else if ( it2.key() == QLatin1String("dc") ) {
|
||
|
if ( mail.isEmpty() ) {
|
||
|
mail = tmp;
|
||
|
} else {
|
||
|
if ( wasDC ) {
|
||
|
mail.append( QLatin1String(".") );
|
||
|
} else {
|
||
|
mail.append( QLatin1String("@") );
|
||
|
}
|
||
|
mail.append( tmp );
|
||
|
}
|
||
|
wasDC = true;
|
||
|
} else if ( it2.key() == QLatin1String("mail") ) {
|
||
|
mail = tmp;
|
||
|
KLDAP::LdapAttrValue::ConstIterator it3 = it2.value().constBegin();
|
||
|
for ( ; it3 != it2.value().constEnd(); ++it3 ) {
|
||
|
mails.append( QString::fromUtf8( (*it3).data(), (*it3).size() ) );
|
||
|
}
|
||
|
} else if ( it2.key() == QLatin1String("givenName") ) {
|
||
|
givenname = tmp;
|
||
|
} else if ( it2.key() == QLatin1String("sn") ) {
|
||
|
sn = tmp;
|
||
|
} else if ( it2.key() == QLatin1String("objectClass") &&
|
||
|
(tmp == QLatin1String("groupOfNames") || tmp == QLatin1String("kolabGroupOfNames")) ) {
|
||
|
isDistributionList = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( mails.isEmpty() ) {
|
||
|
if ( !mail.isEmpty() ) {
|
||
|
mails.append( mail );
|
||
|
}
|
||
|
if ( isDistributionList ) {
|
||
|
//kDebug(5300) <<"\n\nLdapClientSearch::makeSearchData() found a list:" << name;
|
||
|
ret.append( name );
|
||
|
// following lines commented out for bugfixing kolab issue #177:
|
||
|
//
|
||
|
// Unlike we thought previously we may NOT append the server name here.
|
||
|
//
|
||
|
// The right server is found by the SMTP server instead: Kolab users
|
||
|
// must use the correct SMTP server, by definition.
|
||
|
//
|
||
|
//mail = (*it1).client->base().simplified();
|
||
|
//mail.replace( ",dc=", ".", false );
|
||
|
//if( mail.startsWith("dc=", false) )
|
||
|
// mail.remove(0, 3);
|
||
|
//mail.prepend( '@' );
|
||
|
//mail.prepend( name );
|
||
|
//mail = name;
|
||
|
} else {
|
||
|
continue; // nothing, bad entry
|
||
|
}
|
||
|
} else if ( name.isEmpty() ) {
|
||
|
ret.append( mail );
|
||
|
} else {
|
||
|
ret.append( QString::fromLatin1( "%1 <%2>" ).arg( name ).arg( mail ) );
|
||
|
}
|
||
|
|
||
|
LdapResult sr;
|
||
|
sr.dn = (*it1).object.dn();
|
||
|
sr.clientNumber = (*it1).client->clientNumber();
|
||
|
sr.completionWeight = (*it1).client->completionWeight();
|
||
|
sr.name = name;
|
||
|
sr.email = mails;
|
||
|
resList.append( sr );
|
||
|
}
|
||
|
|
||
|
mResults.clear();
|
||
|
}
|
||
|
|
||
|
bool LdapClientSearch::isAvailable() const
|
||
|
{
|
||
|
return !d->mNoLDAPLookup;
|
||
|
}
|
||
|
|
||
|
#include "moc_ldapclientsearch.cpp"
|