|
|
|
/* 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>
|
|
|
|
*
|
|
|
|
* This file is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This file 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqtextstream.h>
|
|
|
|
#include <tqurl.h>
|
|
|
|
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <tdeconfig.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kmdcodec.h>
|
|
|
|
#include <kprotocolinfo.h>
|
|
|
|
|
|
|
|
#include "ldapclient.h"
|
|
|
|
#include "ldif.h"
|
|
|
|
#include "ldapurl.h"
|
|
|
|
|
|
|
|
using namespace KABC;
|
|
|
|
|
|
|
|
class LdapClient::LdapClientPrivate{
|
|
|
|
public:
|
|
|
|
TQString bindDN;
|
|
|
|
TQString pwdBindDN;
|
|
|
|
LDIF ldif;
|
|
|
|
};
|
|
|
|
|
|
|
|
TQString LdapObject::toString() const
|
|
|
|
{
|
|
|
|
TQString result = TQString::fromLatin1( "\ndn: %1\n" ).arg( dn );
|
|
|
|
for ( LdapAttrMap::ConstIterator it = attrs.begin(); it != attrs.end(); ++it ) {
|
|
|
|
TQString attr = it.key();
|
|
|
|
for ( LdapAttrValue::ConstIterator it2 = (*it).begin(); it2 != (*it).end(); ++it2 ) {
|
|
|
|
result += TQString::fromUtf8( LDIF::assembleLine( attr, *it2, 76 ) ) + "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapObject::clear()
|
|
|
|
{
|
|
|
|
dn = TQString::null;
|
|
|
|
attrs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapObject::assign( const LdapObject& that )
|
|
|
|
{
|
|
|
|
if ( &that != this ) {
|
|
|
|
dn = that.dn;
|
|
|
|
attrs = that.attrs;
|
|
|
|
client = that.client;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LdapClient::LdapClient( TQObject* parent, const char* name )
|
|
|
|
: TQObject( parent, name ), mJob( 0 ), mActive( false )
|
|
|
|
{
|
|
|
|
d = new LdapClientPrivate;
|
|
|
|
}
|
|
|
|
|
|
|
|
LdapClient::~LdapClient()
|
|
|
|
{
|
|
|
|
cancelQuery();
|
|
|
|
delete d; d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::setHost( const TQString& host )
|
|
|
|
{
|
|
|
|
mHost = host;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::setPort( const TQString& port )
|
|
|
|
{
|
|
|
|
mPort = port;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::setBase( const TQString& base )
|
|
|
|
{
|
|
|
|
mBase = base;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::setBindDN( const TQString& bindDN )
|
|
|
|
{
|
|
|
|
d->bindDN = bindDN;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::setPwdBindDN( const TQString& pwdBindDN )
|
|
|
|
{
|
|
|
|
d->pwdBindDN = pwdBindDN;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::setAttrs( const TQStringList& attrs )
|
|
|
|
{
|
|
|
|
mAttrs = attrs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::startQuery( const TQString& filter )
|
|
|
|
{
|
|
|
|
cancelQuery();
|
|
|
|
LDAPUrl url;
|
|
|
|
|
|
|
|
url.setProtocol( "ldap" );
|
|
|
|
url.setUser( d->bindDN );
|
|
|
|
url.setPass( d->pwdBindDN );
|
|
|
|
url.setHost( mHost );
|
|
|
|
url.setPort( mPort.toUInt() );
|
|
|
|
url.setDn( mBase );
|
|
|
|
url.setAttributes( mAttrs );
|
|
|
|
url.setScope( mScope == "one" ? LDAPUrl::One : LDAPUrl::Sub );
|
|
|
|
url.setFilter( "("+filter+")" );
|
|
|
|
|
|
|
|
kdDebug(5700) << "Doing query: " << url.prettyURL() << endl;
|
|
|
|
|
|
|
|
startParseLDIF();
|
|
|
|
mActive = true;
|
|
|
|
mJob = TDEIO::get( url, false, false );
|
|
|
|
connect( mJob, TQT_SIGNAL( data( TDEIO::Job*, const TQByteArray& ) ),
|
|
|
|
this, TQT_SLOT( slotData( TDEIO::Job*, const TQByteArray& ) ) );
|
|
|
|
connect( mJob, TQT_SIGNAL( infoMessage( TDEIO::Job*, const TQString& ) ),
|
|
|
|
this, TQT_SLOT( slotInfoMessage( TDEIO::Job*, const TQString& ) ) );
|
|
|
|
connect( mJob, TQT_SIGNAL( result( TDEIO::Job* ) ),
|
|
|
|
this, TQT_SLOT( slotDone() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::cancelQuery()
|
|
|
|
{
|
|
|
|
if ( mJob ) {
|
|
|
|
mJob->kill();
|
|
|
|
mJob = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mActive = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::slotData( TDEIO::Job*, const TQByteArray& data )
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG // don't create the QString
|
|
|
|
// TQString str( data );
|
|
|
|
// kdDebug(5700) << "LdapClient: Got \"" << str << "\"\n";
|
|
|
|
#endif
|
|
|
|
parseLDIF( data );
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::slotInfoMessage( TDEIO::Job*, const TQString & )
|
|
|
|
{
|
|
|
|
//tqDebug("Job said \"%s\"", info.latin1());
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::slotDone()
|
|
|
|
{
|
|
|
|
endParseLDIF();
|
|
|
|
mActive = false;
|
|
|
|
#if 0
|
|
|
|
for ( TQValueList<LdapObject>::Iterator it = mObjects.begin(); it != mObjects.end(); ++it ) {
|
|
|
|
tqDebug( (*it).toString().latin1() );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
int err = mJob->error();
|
|
|
|
if ( err && err != TDEIO::ERR_USER_CANCELED ) {
|
|
|
|
emit error( TDEIO::buildErrorString( err, TQString("%1:%2").arg( mHost ).arg( mPort ) ) );
|
|
|
|
}
|
|
|
|
emit done();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::startParseLDIF()
|
|
|
|
{
|
|
|
|
mCurrentObject.clear();
|
|
|
|
mLastAttrName = 0;
|
|
|
|
mLastAttrValue = 0;
|
|
|
|
mIsBase64 = false;
|
|
|
|
d->ldif.startParsing();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::endParseLDIF()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapClient::parseLDIF( const TQByteArray& data )
|
|
|
|
{
|
|
|
|
if ( data.size() ) {
|
|
|
|
d->ldif.setLDIF( data );
|
|
|
|
} else {
|
|
|
|
d->ldif.endLDIF();
|
|
|
|
}
|
|
|
|
|
|
|
|
LDIF::ParseVal ret;
|
|
|
|
TQString name;
|
|
|
|
do {
|
|
|
|
ret = d->ldif.nextItem();
|
|
|
|
switch ( ret ) {
|
|
|
|
case LDIF::Item:
|
|
|
|
{
|
|
|
|
name = d->ldif.attr();
|
|
|
|
// Must make a copy! TQByteArray is explicitely shared
|
|
|
|
TQByteArray value = d->ldif.val().copy();
|
|
|
|
mCurrentObject.attrs[ name ].append( value );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LDIF::EndEntry:
|
|
|
|
mCurrentObject.dn = d->ldif.dn();
|
|
|
|
mCurrentObject.client = this;
|
|
|
|
emit result( mCurrentObject );
|
|
|
|
mCurrentObject.clear();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while ( ret != LDIF::MoreData );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString LdapClient::bindDN() const
|
|
|
|
{
|
|
|
|
return d->bindDN;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString LdapClient::pwdBindDN() const
|
|
|
|
{
|
|
|
|
return d->pwdBindDN;
|
|
|
|
}
|
|
|
|
|
|
|
|
LdapSearch::LdapSearch()
|
|
|
|
: mActiveClients( 0 ), mNoLDAPLookup( false )
|
|
|
|
{
|
|
|
|
if ( !KProtocolInfo::isKnownProtocol( KURL("ldap://localhost") ) ) {
|
|
|
|
mNoLDAPLookup = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// stolen from KAddressBook
|
|
|
|
TDEConfig config( "kabldaprc", true );
|
|
|
|
config.setGroup( "LDAP" );
|
|
|
|
int numHosts = config.readUnsignedNumEntry( "NumSelectedHosts");
|
|
|
|
if ( !numHosts ) {
|
|
|
|
mNoLDAPLookup = true;
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
for ( int j = 0; j < numHosts; j++ ) {
|
|
|
|
LdapClient* ldapClient = new LdapClient( this );
|
|
|
|
|
|
|
|
TQString host = config.readEntry( TQString( "SelectedHost%1" ).arg( j ), "" ).stripWhiteSpace();
|
|
|
|
if ( !host.isEmpty() )
|
|
|
|
ldapClient->setHost( host );
|
|
|
|
|
|
|
|
TQString port = TQString::number( config.readUnsignedNumEntry( TQString( "SelectedPort%1" ).arg( j ) ) );
|
|
|
|
if ( !port.isEmpty() )
|
|
|
|
ldapClient->setPort( port );
|
|
|
|
|
|
|
|
TQString base = config.readEntry( TQString( "SelectedBase%1" ).arg( j ), "" ).stripWhiteSpace();
|
|
|
|
if ( !base.isEmpty() )
|
|
|
|
ldapClient->setBase( base );
|
|
|
|
|
|
|
|
TQString bindDN = config.readEntry( TQString( "SelectedBind%1" ).arg( j ) ).stripWhiteSpace();
|
|
|
|
if ( !bindDN.isEmpty() )
|
|
|
|
ldapClient->setBindDN( bindDN );
|
|
|
|
|
|
|
|
TQString pwdBindDN = config.readEntry( TQString( "SelectedPwdBind%1" ).arg( j ) );
|
|
|
|
if ( !pwdBindDN.isEmpty() )
|
|
|
|
ldapClient->setPwdBindDN( pwdBindDN );
|
|
|
|
|
|
|
|
TQStringList attrs;
|
|
|
|
attrs << "cn" << "mail" << "givenname" << "sn";
|
|
|
|
ldapClient->setAttrs( attrs );
|
|
|
|
|
|
|
|
connect( ldapClient, TQT_SIGNAL( result( const KABC::LdapObject& ) ),
|
|
|
|
this, TQT_SLOT( slotLDAPResult( const KABC::LdapObject& ) ) );
|
|
|
|
connect( ldapClient, TQT_SIGNAL( done() ),
|
|
|
|
this, TQT_SLOT( slotLDAPDone() ) );
|
|
|
|
connect( ldapClient, TQT_SIGNAL( error( const TQString& ) ),
|
|
|
|
this, TQT_SLOT( slotLDAPError( const TQString& ) ) );
|
|
|
|
|
|
|
|
mClients.append( ldapClient );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
connect( &mDataTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotDataTimer() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::startSearch( const TQString& txt )
|
|
|
|
{
|
|
|
|
if ( mNoLDAPLookup )
|
|
|
|
return;
|
|
|
|
|
|
|
|
cancelSearch();
|
|
|
|
|
|
|
|
int pos = txt.find( '\"' );
|
|
|
|
if( pos >= 0 )
|
|
|
|
{
|
|
|
|
++pos;
|
|
|
|
int pos2 = txt.find( '\"', pos );
|
|
|
|
if( pos2 >= 0 )
|
|
|
|
mSearchText = txt.mid( pos , pos2 - pos );
|
|
|
|
else
|
|
|
|
mSearchText = txt.mid( pos );
|
|
|
|
} else
|
|
|
|
mSearchText = txt;
|
|
|
|
|
|
|
|
TQString filter = TQString( "|(cn=%1*)(mail=%2*)(givenName=%3*)(sn=%4*)" )
|
|
|
|
.arg( mSearchText ).arg( mSearchText ).arg( mSearchText ).arg( mSearchText );
|
|
|
|
|
|
|
|
TQValueList< LdapClient* >::Iterator it;
|
|
|
|
for ( it = mClients.begin(); it != mClients.end(); ++it ) {
|
|
|
|
(*it)->startQuery( filter );
|
|
|
|
++mActiveClients;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::cancelSearch()
|
|
|
|
{
|
|
|
|
TQValueList< LdapClient* >::Iterator it;
|
|
|
|
for ( it = mClients.begin(); it != mClients.end(); ++it )
|
|
|
|
(*it)->cancelQuery();
|
|
|
|
|
|
|
|
mActiveClients = 0;
|
|
|
|
mResults.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::slotLDAPResult( const KABC::LdapObject& obj )
|
|
|
|
{
|
|
|
|
mResults.append( obj );
|
|
|
|
if ( !mDataTimer.isActive() )
|
|
|
|
mDataTimer.start( 500, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::slotLDAPError( const TQString& )
|
|
|
|
{
|
|
|
|
slotLDAPDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::slotLDAPDone()
|
|
|
|
{
|
|
|
|
if ( --mActiveClients > 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::slotDataTimer()
|
|
|
|
{
|
|
|
|
TQStringList lst;
|
|
|
|
LdapResultList reslist;
|
|
|
|
makeSearchData( lst, reslist );
|
|
|
|
if ( !lst.isEmpty() )
|
|
|
|
emit searchData( lst );
|
|
|
|
if ( !reslist.isEmpty() )
|
|
|
|
emit searchData( reslist );
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::finish()
|
|
|
|
{
|
|
|
|
mDataTimer.stop();
|
|
|
|
|
|
|
|
slotDataTimer(); // emit final bunch of data
|
|
|
|
emit searchDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LdapSearch::makeSearchData( TQStringList& ret, LdapResultList& resList )
|
|
|
|
{
|
|
|
|
TQString search_text_upper = mSearchText.upper();
|
|
|
|
|
|
|
|
TQValueList< KABC::LdapObject >::ConstIterator it1;
|
|
|
|
for ( it1 = mResults.begin(); it1 != mResults.end(); ++it1 ) {
|
|
|
|
TQString name, mail, givenname, sn;
|
|
|
|
|
|
|
|
LdapAttrMap::ConstIterator it2;
|
|
|
|
for ( it2 = (*it1).attrs.begin(); it2 != (*it1).attrs.end(); ++it2 ) {
|
|
|
|
TQString tmp = TQString::fromUtf8( (*it2).first(), (*it2).first().size() );
|
|
|
|
if ( it2.key() == "cn" )
|
|
|
|
name = tmp; // TODO loop?
|
|
|
|
else if( it2.key() == "mail" )
|
|
|
|
mail = tmp;
|
|
|
|
else if( it2.key() == "givenName" )
|
|
|
|
givenname = tmp;
|
|
|
|
else if( it2.key() == "sn" )
|
|
|
|
sn = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( mail.isEmpty())
|
|
|
|
continue; // nothing, bad entry
|
|
|
|
else if ( name.isEmpty() )
|
|
|
|
ret.append( mail );
|
|
|
|
else {
|
|
|
|
kdDebug(5700) << "<" << name << "><" << mail << ">" << endl;
|
|
|
|
ret.append( TQString( "%1 <%2>" ).arg( name ).arg( mail ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
LdapResult sr;
|
|
|
|
sr.clientNumber = mClients.findIndex( (*it1).client );
|
|
|
|
sr.name = name;
|
|
|
|
sr.email = mail;
|
|
|
|
resList.append( sr );
|
|
|
|
}
|
|
|
|
|
|
|
|
mResults.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LdapSearch::isAvailable() const
|
|
|
|
{
|
|
|
|
return !mNoLDAPLookup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "ldapclient.moc"
|