/* kldapclient.cpp - LDAP access * Copyright (C) 2002 Klarälvdalens Datakonsult AB * * Author: Steffen Hansen * * Ported to KABC by Daniel Molkentin * * Copyright (C) 2013 Laurent Montel * * 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 #include #include #include #include #include #include #include #include #include #include #include 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 mClients; QStringList mAttributes; QString mSearchText; QString mFilter; QTimer mDataTimer; int mActiveClients; bool mNoLDAPLookup; QList 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 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::Iterator it; QList::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::Iterator it; QList::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"