libkdepim Library API Documentation

ldapsearchdialog.cpp

00001 /* ldapsearchdialogimpl.cpp - LDAP access
00002  *      Copyright (C) 2002 Klar�vdalens Datakonsult AB
00003  *
00004  *      Author: Steffen Hansen <hansen@kde.org>
00005  *
00006  * This file is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This file is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00019  */
00020 
00021 
00022 #include <qcheckbox.h>
00023 #include <qgroupbox.h>
00024 #include <qheader.h>
00025 #include <qlabel.h>
00026 #include <qlayout.h>
00027 #include <qlistview.h>
00028 #include <qpushbutton.h>
00029 
00030 #include <kabc/addresslineedit.h>
00031 #include <kapplication.h>
00032 #include <kcombobox.h>
00033 #include <kconfig.h>
00034 #include <klineedit.h>
00035 #include <klocale.h>
00036 #include <kmessagebox.h>
00037 
00038 #include "ldapsearchdialog.h"
00039 
00040 using namespace KPIM;
00041 
00042 static QString asUtf8( const QByteArray &val )
00043 {
00044   if ( val.isEmpty() )
00045     return QString::null;
00046 
00047   const char *data = val.data();
00048 
00049   //QString::fromUtf8() bug workaround
00050   if ( data[ val.size() - 1 ] == '\0' )
00051     return QString::fromUtf8( data, val.size() - 1 );
00052   else
00053     return QString::fromUtf8( data, val.size() );
00054 }
00055 
00056 static QString join( const KPIM::LdapAttrValue& lst, const QString& sep )
00057 {
00058   QString res;
00059   bool alredy = false;
00060   for ( KPIM::LdapAttrValue::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
00061     if ( alredy )
00062       res += sep;
00063     alredy = TRUE;
00064     res += asUtf8( *it );
00065   }
00066   return res;
00067 }
00068 
00069 static QMap<QString, QString>& adrbookattr2ldap()
00070 {
00071   static QMap<QString, QString> keys;
00072 
00073   if ( keys.isEmpty() ) {
00074     keys[ i18n( "Title" ) ] = "title";
00075     keys[ i18n( "Full Name" ) ] = "cn";
00076     keys[ i18n( "Email" ) ] = "mail";
00077     keys[ i18n( "Home Number" ) ] = "homePhone";
00078     keys[ i18n( "Work Number" ) ] = "telephoneNumber";
00079     keys[ i18n( "Mobile Number" ) ] = "mobile";
00080     keys[ i18n( "Fax Number" ) ] = "facsimileTelephoneNumber";
00081     keys[ i18n( "Pager" ) ] = "pager";
00082     keys[ i18n( "Street") ] = "street";
00083     keys[ i18n( "State" ) ] = "st";
00084     keys[ i18n( "Country" ) ] = "co";
00085     keys[ i18n( "City" ) ] = "l";
00086     keys[ i18n( "Organization" ) ] = "o";
00087     keys[ i18n( "Company" ) ] = "Company";
00088     keys[ i18n( "Department" ) ] = "department";
00089     keys[ i18n( "Zip Code" ) ] = "postalCode";
00090     keys[ i18n( "Postal Address" ) ] = "postalAddress";
00091     keys[ i18n( "Description" ) ] = "description";
00092     keys[ i18n( "User ID" ) ] = "uid";
00093   }
00094   return keys;
00095 }
00096 
00097 class ContactListItem : public QListViewItem
00098 {
00099   public:
00100     ContactListItem( QListView* parent, const KPIM::LdapAttrMap& attrs )
00101       : QListViewItem( parent ), mAttrs( attrs )
00102     { }
00103 
00104     KPIM::LdapAttrMap mAttrs;
00105 
00106     virtual QString text( int col ) const
00107     {
00108       // Look up a suitable attribute for column col
00109       const QString colName = listView()->columnText( col );
00110       const QString ldapAttrName = adrbookattr2ldap()[ colName ];
00111       return join( mAttrs[ ldapAttrName ], ", " );
00112     }
00113 };
00114 
00115 LDAPSearchDialog::LDAPSearchDialog( QWidget* parent, const char* name )
00116   : KDialogBase( Plain, i18n( "Search for Addresses in Directory" ), Help | User1 |
00117     User2 | User3 | Cancel, Default, parent, name, false, true )
00118 {
00119   setButtonCancel( KStdGuiItem::close() );
00120   QFrame *page = plainPage();
00121   QVBoxLayout *topLayout = new QVBoxLayout( page, marginHint(), spacingHint() );
00122 
00123   QGroupBox *groupBox = new QGroupBox( i18n( "Search for Addresses in Directory" ),
00124                                        page );
00125   groupBox->setFrameShape( QGroupBox::Box );
00126   groupBox->setFrameShadow( QGroupBox::Sunken );
00127   groupBox->setColumnLayout( 0, Qt::Vertical );
00128   QGridLayout *boxLayout = new QGridLayout( groupBox->layout(), 2,
00129                                             5, spacingHint() );
00130   boxLayout->setColStretch( 1, 1 );
00131 
00132   QLabel *label = new QLabel( i18n( "Search for:" ), groupBox );
00133   boxLayout->addWidget( label, 0, 0 );
00134 
00135   mSearchEdit = new KLineEdit( groupBox );
00136   boxLayout->addWidget( mSearchEdit, 0, 1 );
00137   label->setBuddy( mSearchEdit );
00138 
00139   label = new QLabel( i18n( "in" ), groupBox );
00140   boxLayout->addWidget( label, 0, 2 );
00141 
00142   mFilterCombo = new KComboBox( groupBox );
00143   mFilterCombo->insertItem( i18n( "Name" ) );
00144   mFilterCombo->insertItem( i18n( "Email" ) );
00145   mFilterCombo->insertItem( i18n( "Home Number" ) );
00146   mFilterCombo->insertItem( i18n( "Work Number" ) );
00147   boxLayout->addWidget( mFilterCombo, 0, 3 );
00148 
00149   QSize buttonSize;
00150   mSearchButton = new QPushButton( i18n( "Stop" ), groupBox );
00151   buttonSize = mSearchButton->sizeHint();
00152   mSearchButton->setText( i18n( "Search" ) );
00153   if ( buttonSize.width() < mSearchButton->sizeHint().width() )
00154     buttonSize = mSearchButton->sizeHint();
00155   mSearchButton->setFixedWidth( buttonSize.width() );
00156 
00157   mSearchButton->setDefault( true );
00158   boxLayout->addWidget( mSearchButton, 0, 4 );
00159 
00160   mRecursiveCheckbox = new QCheckBox( i18n( "Recursive search" ), groupBox  );
00161   mRecursiveCheckbox->setChecked( true );
00162   boxLayout->addMultiCellWidget( mRecursiveCheckbox, 1, 1, 0, 4 );
00163 
00164   mSearchType = new KComboBox( groupBox );
00165   mSearchType->insertItem( i18n( "Contains" ) );
00166   mSearchType->insertItem( i18n( "Starts With" ) );
00167   boxLayout->addMultiCellWidget( mSearchType, 1, 1, 3, 4 );
00168 
00169   topLayout->addWidget( groupBox );
00170 
00171   mResultListView = new QListView( page );
00172   mResultListView->setSelectionMode( QListView::Multi );
00173   mResultListView->setAllColumnsShowFocus( true );
00174   mResultListView->setShowSortIndicator( true );
00175   topLayout->addWidget( mResultListView );
00176 
00177   resize( QSize( 600, 400).expandedTo( minimumSizeHint() ) );
00178 
00179   setButtonText( User1, i18n( "Unselect All" ) );
00180   setButtonText( User2, i18n( "Select All" ) );
00181   setButtonText( User3, i18n( "Add Selected" ) );
00182 
00183   mNumHosts = 0;
00184   mIsOK = false;
00185 
00186   connect( mRecursiveCheckbox, SIGNAL( toggled( bool ) ),
00187        this, SLOT( slotSetScope( bool ) ) );
00188   connect( mSearchButton, SIGNAL( clicked() ),
00189        this, SLOT( slotStartSearch() ) );
00190 
00191   setTabOrder(mSearchEdit, mFilterCombo);
00192   setTabOrder(mFilterCombo, mSearchButton);
00193   mSearchEdit->setFocus();
00194 
00195   restoreSettings();
00196 }
00197 
00198 LDAPSearchDialog::~LDAPSearchDialog()
00199 {
00200   saveSettings();
00201 }
00202 
00203 void LDAPSearchDialog::restoreSettings()
00204 {
00205   // Create one KPIM::LdapClient per selected server and configure it.
00206 
00207   // First clean the list to make sure it is empty at
00208   // the beginning of the process
00209   mLdapClientList.setAutoDelete( true );
00210   mLdapClientList.clear();
00211 
00212   KConfig kabConfig( "kaddressbookrc" );
00213   kabConfig.setGroup( "LDAPSearch" );
00214   mSearchType->setCurrentItem( kabConfig.readNumEntry( "SearchType", 0 ) );
00215 
00216   // then read the config file and register all selected
00217   // server in the list
00218   KConfig* config = KABC::AddressLineEdit::config(); // singleton kabldaprc config object
00219   KConfigGroupSaver saver( config, "LDAP" );
00220   mNumHosts = config->readUnsignedNumEntry( "NumSelectedHosts" );
00221   if ( !mNumHosts ) {
00222     KMessageBox::error( this, i18n( "You must select a LDAP server before searching.\nYou can do this from the menu Settings/Configure KAddressBook." ) );
00223     mIsOK = false;
00224   } else {
00225     mIsOK = true;
00226     for ( int j = 0; j < mNumHosts; ++j ) {
00227       KPIM::LdapClient* ldapClient = new KPIM::LdapClient( 0, this, "ldapclient" );
00228 
00229       QString host = config->readEntry( QString( "SelectedHost%1" ).arg( j ), "" );
00230       if ( !host.isEmpty() )
00231         ldapClient->setHost( host );
00232 
00233       QString port = QString::number( config->readUnsignedNumEntry( QString( "SelectedPort%1" ).arg( j ) ) );
00234       if ( !port.isEmpty() )
00235         ldapClient->setPort( port );
00236 
00237       QString base = config->readEntry( QString( "SelectedBase%1" ).arg( j ), "" );
00238       if ( !base.isEmpty() )
00239         ldapClient->setBase( base );
00240 
00241       QString bindDN = config->readEntry( QString( "SelectedBind%1" ).arg( j ), "" );
00242       if ( !bindDN.isEmpty() )
00243         ldapClient->setBindDN( bindDN );
00244 
00245       QString pwdBindDN = config->readEntry( QString( "SelectedPwdBind%1" ).arg( j ), "" );
00246       if ( !pwdBindDN.isEmpty() )
00247         ldapClient->setPwdBindDN( pwdBindDN );
00248 
00249       QStringList attrs;
00250 
00251       for ( QMap<QString,QString>::Iterator it = adrbookattr2ldap().begin(); it != adrbookattr2ldap().end(); ++it )
00252         attrs << *it;
00253 
00254       ldapClient->setAttrs( attrs );
00255 
00256       connect( ldapClient, SIGNAL( result( const KPIM::LdapObject& ) ),
00257            this, SLOT( slotAddResult( const KPIM::LdapObject& ) ) );
00258       connect( ldapClient, SIGNAL( done() ),
00259            this, SLOT( slotSearchDone() ) );
00260       connect( ldapClient, SIGNAL( error( const QString& ) ),
00261            this, SLOT( slotError( const QString& ) ) );
00262 
00263       mLdapClientList.append( ldapClient );
00264     }
00265 
00267     while ( mResultListView->header()->count() > 0 ) {
00268       mResultListView->removeColumn(0);
00269     }
00270 
00271     mResultListView->addColumn( i18n( "Full Name" ) );
00272     mResultListView->addColumn( i18n( "Email" ) );
00273     mResultListView->addColumn( i18n( "Home Number" ) );
00274     mResultListView->addColumn( i18n( "Work Number" ) );
00275     mResultListView->addColumn( i18n( "Mobile Number" ) );
00276     mResultListView->addColumn( i18n( "Fax Number" ) );
00277     mResultListView->addColumn( i18n( "Company" ) );
00278     mResultListView->addColumn( i18n( "Organization" ) );
00279     mResultListView->addColumn( i18n( "Street" ) );
00280     mResultListView->addColumn( i18n( "State" ) );
00281     mResultListView->addColumn( i18n( "Country" ) );
00282     mResultListView->addColumn( i18n( "Zip Code" ) );
00283     mResultListView->addColumn( i18n( "Postal Address" ) );
00284     mResultListView->addColumn( i18n( "City" ) );
00285     mResultListView->addColumn( i18n( "Department" ) );
00286     mResultListView->addColumn( i18n( "Description" ) );
00287     mResultListView->addColumn( i18n( "User ID" ) );
00288     mResultListView->addColumn( i18n( "Title" ) );
00289 
00290     mResultListView->clear();
00291   }
00292 }
00293 
00294 void LDAPSearchDialog::saveSettings()
00295 {
00296   KConfig config( "kaddressbookrc" );
00297   config.setGroup( "LDAPSearch" );
00298   config.writeEntry( "SearchType", mSearchType->currentItem() );
00299   config.sync();
00300 }
00301 
00302 void LDAPSearchDialog::cancelQuery()
00303 {
00304   for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
00305     client->cancelQuery();
00306   }
00307 }
00308 
00309 void LDAPSearchDialog::slotAddResult( const KPIM::LdapObject& obj )
00310 {
00311   new ContactListItem( mResultListView, obj.attrs );
00312 }
00313 
00314 void LDAPSearchDialog::slotSetScope( bool rec )
00315 {
00316   for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
00317     if ( rec )
00318       client->setScope( "sub" );
00319     else
00320       client->setScope( "one" );
00321   }
00322 }
00323 
00324 QString LDAPSearchDialog::makeFilter( const QString& query, const QString& attr,
00325                                       bool startsWith )
00326 {
00327   /* The reasoning behind this filter is:
00328    * If it's a person, or a distlist, show it, even if it doesn't have an email address.
00329    * If it's not a person, or a distlist, only show it if it has an email attribute.
00330    * This allows both resource accounts with an email address which are not a person and
00331    * person entries without an email address to show up, while still not showing things
00332    * like structural entries in the ldap tree. */
00333   QString result( "&(|(objectclass=person)(objectclass=groupofnames)(mail=*))(" );
00334   if( query.isEmpty() )
00335     // Return a filter that matches everything
00336     return result + "|(cn=*)(sn=*)" + ")";
00337 
00338   if ( attr == i18n( "Name" ) ) {
00339     result += startsWith ? "|(cn=%1*)(sn=%2*)" : "|(cn=*%1*)(sn=*%2*)";
00340     result = result.arg( query ).arg( query );
00341   } else {
00342     result += (startsWith ? "%1=%2*" : "%1=*%2*");
00343     if ( attr == i18n( "Email" ) ) {
00344       result = result.arg( "mail" ).arg( query );
00345     } else if ( attr == i18n( "Home Number" ) ) {
00346       result = result.arg( "homePhone" ).arg( query );
00347     } else if ( attr == i18n( "Work Number" ) ) {
00348       result = result.arg( "telephoneNumber" ).arg( query );
00349     } else {
00350       // Error?
00351       result = QString::null;
00352       return result;
00353     }
00354   }
00355   result += ")";
00356   return result;
00357 }
00358 
00359 void LDAPSearchDialog::slotStartSearch()
00360 {
00361   cancelQuery();
00362 
00363   QApplication::setOverrideCursor( Qt::waitCursor );
00364   mSearchButton->setText( i18n( "Stop" ) );
00365 
00366   disconnect( mSearchButton, SIGNAL( clicked() ),
00367               this, SLOT( slotStartSearch() ) );
00368   connect( mSearchButton, SIGNAL( clicked() ),
00369            this, SLOT( slotStopSearch() ) );
00370 
00371   bool startsWith = (mSearchType->currentItem() == 1);
00372 
00373   QString filter = makeFilter( mSearchEdit->text().stripWhiteSpace(), mFilterCombo->currentText(), startsWith );
00374 
00375    // loop in the list and run the KPIM::LdapClients
00376   mResultListView->clear();
00377   for( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
00378     client->startQuery( filter );
00379   }
00380 
00381   saveSettings();
00382 }
00383 
00384 void LDAPSearchDialog::slotStopSearch()
00385 {
00386   cancelQuery();
00387   slotSearchDone();
00388 }
00389 
00390 void LDAPSearchDialog::slotSearchDone()
00391 {
00392   // If there are no more active clients, we are done.
00393   for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
00394     if ( client->isActive() )
00395       return;
00396   }
00397 
00398   disconnect( mSearchButton, SIGNAL( clicked() ),
00399               this, SLOT( slotStopSearch() ) );
00400   connect( mSearchButton, SIGNAL( clicked() ),
00401            this, SLOT( slotStartSearch() ) );
00402 
00403   mSearchButton->setText( i18n( "Search" ) );
00404   QApplication::restoreOverrideCursor();
00405 }
00406 
00407 void LDAPSearchDialog::slotError( const QString& error )
00408 {
00409   QApplication::restoreOverrideCursor();
00410   KMessageBox::error( this, error );
00411 }
00412 
00413 void LDAPSearchDialog::closeEvent( QCloseEvent* e )
00414 {
00415   slotStopSearch();
00416   e->accept();
00417 }
00418 
00423 QString LDAPSearchDialog::selectedEMails() const
00424 {
00425   QStringList result;
00426   ContactListItem* cli = static_cast<ContactListItem*>( mResultListView->firstChild() );
00427   while ( cli ) {
00428     if ( cli->isSelected() ) {
00429       QString email = asUtf8( cli->mAttrs[ "mail" ].first() ).stripWhiteSpace();
00430       if ( !email.isEmpty() ) {
00431         QString name = asUtf8( cli->mAttrs[ "cn" ].first() ).stripWhiteSpace();
00432         if ( name.isEmpty() ) {
00433           result << email;
00434         } else {
00435           result << name + " <" + email + ">";
00436         }
00437       }
00438     }
00439     cli = static_cast<ContactListItem*>( cli->nextSibling() );
00440   }
00441 
00442   return result.join( ", " );
00443 }
00444 
00445 void LDAPSearchDialog::slotHelp()
00446 {
00447   kapp->invokeHelp( "ldap-queries" );
00448 }
00449 
00450 void LDAPSearchDialog::slotUser1()
00451 {
00452   mResultListView->selectAll( false );
00453 }
00454 
00455 void LDAPSearchDialog::slotUser2()
00456 {
00457   mResultListView->selectAll( true );
00458 }
00459 
00460 void LDAPSearchDialog::slotUser3()
00461 {
00462   emit addresseesAdded();
00463 }
00464 
00465 #include "ldapsearchdialog.moc"
KDE Logo
This file is part of the documentation for libkdepim Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Oct 17 09:53:57 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003