kaddressbook

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <addresseelineedit.h>
00031 #include <kapplication.h>
00032 #include <kbuttonbox.h>
00033 #include <kcombobox.h>
00034 #include <kconfig.h>
00035 #include <klineedit.h>
00036 #include <klocale.h>
00037 #include <kmessagebox.h>
00038 
00039 #include "kabcore.h"
00040 #include "ldapsearchdialog.h"
00041 #include "kablock.h"
00042 
00043 static QString asUtf8( const QByteArray &val )
00044 {
00045   if ( val.isEmpty() )
00046     return QString::null;
00047 
00048   const char *data = val.data();
00049 
00050   //QString::fromUtf8() bug workaround
00051   if ( data[ val.size() - 1 ] == '\0' )
00052     return QString::fromUtf8( data, val.size() - 1 );
00053   else
00054     return QString::fromUtf8( data, val.size() );
00055 }
00056 
00057 static QString join( const KPIM::LdapAttrValue& lst, const QString& sep )
00058 {
00059   QString res;
00060   bool alredy = false;
00061   for ( KPIM::LdapAttrValue::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
00062     if ( alredy )
00063       res += sep;
00064     alredy = true;
00065     res += asUtf8( *it );
00066   }
00067   return res;
00068 }
00069 
00070 static QMap<QString, QString>& adrbookattr2ldap()
00071 {
00072   static QMap<QString, QString> keys;
00073 
00074   if ( keys.isEmpty() ) {
00075     keys[ i18n( "Title" ) ] = "title";
00076     keys[ i18n( "Full Name" ) ] = "cn";
00077     keys[ i18n( "Email" ) ] = "mail";
00078     keys[ i18n( "Home Number" ) ] = "homePhone";
00079     keys[ i18n( "Work Number" ) ] = "telephoneNumber";
00080     keys[ i18n( "Mobile Number" ) ] = "mobile";
00081     keys[ i18n( "Fax Number" ) ] = "facsimileTelephoneNumber";
00082     keys[ i18n( "Pager" ) ] = "pager";
00083     keys[ i18n( "Street") ] = "street";
00084     keys[ i18n( "State" ) ] = "st";
00085     keys[ i18n( "Country" ) ] = "co";
00086     keys[ i18n( "City" ) ] = "l";
00087     keys[ i18n( "Organization" ) ] = "o";
00088     keys[ i18n( "Company" ) ] = "Company";
00089     keys[ i18n( "Department" ) ] = "department";
00090     keys[ i18n( "Zip Code" ) ] = "postalCode";
00091     keys[ i18n( "Postal Address" ) ] = "postalAddress";
00092     keys[ i18n( "Description" ) ] = "description";
00093     keys[ i18n( "User ID" ) ] = "uid";
00094   }
00095   return keys;
00096 }
00097 
00098 class ContactListItem : public QListViewItem
00099 {
00100   public:
00101     ContactListItem( QListView* parent, const KPIM::LdapAttrMap& attrs )
00102       : QListViewItem( parent ), mAttrs( attrs )
00103     { }
00104 
00105     KPIM::LdapAttrMap mAttrs;
00106 
00107     virtual QString text( int col ) const
00108     {
00109       // Look up a suitable attribute for column col
00110       const QString colName = listView()->columnText( col );
00111       const QString ldapAttrName = adrbookattr2ldap()[ colName ];
00112       return join( mAttrs[ ldapAttrName ], ", " );
00113     }
00114 };
00115 
00116 LDAPSearchDialog::LDAPSearchDialog( KABC::AddressBook *ab, KABCore *core,
00117                                     QWidget* parent, const char* name )
00118   : KDialogBase( Plain, i18n( "Search for Addresses in Directory" ), Help | User1 |
00119                  Cancel, Default, parent, name, false, true ),
00120     mAddressBook( ab ), mCore( core )
00121 {
00122   setButtonCancel( KStdGuiItem::close() );
00123   QFrame *page = plainPage();
00124   QVBoxLayout *topLayout = new QVBoxLayout( page, marginHint(), spacingHint() );
00125 
00126   QGroupBox *groupBox = new QGroupBox( i18n( "Search for Addresses in Directory" ),
00127                                        page );
00128   groupBox->setFrameShape( QGroupBox::Box );
00129   groupBox->setFrameShadow( QGroupBox::Sunken );
00130   groupBox->setColumnLayout( 0, Qt::Vertical );
00131   QGridLayout *boxLayout = new QGridLayout( groupBox->layout(), 2,
00132                                             5, spacingHint() );
00133   boxLayout->setColStretch( 1, 1 );
00134 
00135   QLabel *label = new QLabel( i18n( "Search for:" ), groupBox );
00136   boxLayout->addWidget( label, 0, 0 );
00137 
00138   mSearchEdit = new KLineEdit( groupBox );
00139   boxLayout->addWidget( mSearchEdit, 0, 1 );
00140   label->setBuddy( mSearchEdit );
00141 
00142   label = new QLabel( i18n( "In LDAP attribute", "in" ), groupBox );
00143   boxLayout->addWidget( label, 0, 2 );
00144 
00145   mFilterCombo = new KComboBox( groupBox );
00146   mFilterCombo->insertItem( i18n( "Name" ) );
00147   mFilterCombo->insertItem( i18n( "Email" ) );
00148   mFilterCombo->insertItem( i18n( "Home Number" ) );
00149   mFilterCombo->insertItem( i18n( "Work Number" ) );
00150   boxLayout->addWidget( mFilterCombo, 0, 3 );
00151 
00152   QSize buttonSize;
00153   mSearchButton = new QPushButton( i18n( "Stop" ), groupBox );
00154   buttonSize = mSearchButton->sizeHint();
00155   mSearchButton->setText( i18n( "&Search" ) );
00156   if ( buttonSize.width() < mSearchButton->sizeHint().width() )
00157     buttonSize = mSearchButton->sizeHint();
00158   mSearchButton->setFixedWidth( buttonSize.width() );
00159 
00160   mSearchButton->setDefault( true );
00161   boxLayout->addWidget( mSearchButton, 0, 4 );
00162 
00163   mRecursiveCheckbox = new QCheckBox( i18n( "Recursive search" ), groupBox  );
00164   mRecursiveCheckbox->setChecked( true );
00165   boxLayout->addMultiCellWidget( mRecursiveCheckbox, 1, 1, 0, 4 );
00166 
00167   mSearchType = new KComboBox( groupBox );
00168   mSearchType->insertItem( i18n( "Contains" ) );
00169   mSearchType->insertItem( i18n( "Starts With" ) );
00170   boxLayout->addMultiCellWidget( mSearchType, 1, 1, 3, 4 );
00171 
00172   topLayout->addWidget( groupBox );
00173 
00174   mResultListView = new QListView( page );
00175   mResultListView->setSelectionMode( QListView::Multi );
00176   mResultListView->setAllColumnsShowFocus( true );
00177   mResultListView->setShowSortIndicator( true );
00178   topLayout->addWidget( mResultListView );
00179 
00180   KButtonBox *buttons = new KButtonBox( page, Qt::Horizontal );
00181   buttons->addButton( i18n( "Select All" ), this, SLOT( slotSelectAll() ) );
00182   buttons->addButton( i18n( "Unselect All" ), this, SLOT( slotUnselectAll() ) );
00183 
00184   topLayout->addWidget( buttons );
00185 
00186   resize( QSize( 600, 400).expandedTo( minimumSizeHint() ) );
00187 
00188   setButtonText( User1, i18n( "Add Selected" ) );
00189 
00190   mNumHosts = 0;
00191   mIsOK = false;
00192 
00193   connect( mRecursiveCheckbox, SIGNAL( toggled( bool ) ),
00194        this, SLOT( slotSetScope( bool ) ) );
00195   connect( mSearchButton, SIGNAL( clicked() ),
00196        this, SLOT( slotStartSearch() ) );
00197 
00198   setTabOrder(mSearchEdit, mFilterCombo);
00199   setTabOrder(mFilterCombo, mSearchButton);
00200   mSearchEdit->setFocus();
00201 
00202   restoreSettings();
00203 }
00204 
00205 LDAPSearchDialog::~LDAPSearchDialog()
00206 {
00207   saveSettings();
00208 }
00209 
00210 void LDAPSearchDialog::restoreSettings()
00211 {
00212   // Create one KPIM::LdapClient per selected server and configure it.
00213 
00214   // First clean the list to make sure it is empty at
00215   // the beginning of the process
00216   mLdapClientList.setAutoDelete( true );
00217   mLdapClientList.clear();
00218 
00219   KConfig kabConfig( "kaddressbookrc" );
00220   kabConfig.setGroup( "LDAPSearch" );
00221   mSearchType->setCurrentItem( kabConfig.readNumEntry( "SearchType", 0 ) );
00222 
00223   // then read the config file and register all selected
00224   // server in the list
00225   KConfig* config = KPIM::LdapSearch::config();
00226   KConfigGroupSaver saver( config, "LDAP" );
00227   mNumHosts = config->readUnsignedNumEntry( "NumSelectedHosts" );
00228   if ( !mNumHosts ) {
00229     KMessageBox::error( this, i18n( "You must select a LDAP server before searching.\nYou can do this from the menu Settings/Configure KAddressBook." ) );
00230     mIsOK = false;
00231   } else {
00232     mIsOK = true;
00233     for ( int j = 0; j < mNumHosts; ++j ) {
00234       KPIM::LdapClient* ldapClient = new KPIM::LdapClient( 0, this, "ldapclient" );
00235       KPIM::LdapServer ldapServer;
00236       KPIM::LdapSearch::readConfig( ldapServer, config, j, true );
00237       ldapClient->setServer( ldapServer );
00238       QStringList attrs;
00239 
00240       for ( QMap<QString,QString>::ConstIterator it = adrbookattr2ldap().begin(); it != adrbookattr2ldap().end(); ++it )
00241         attrs << *it;
00242 
00243       ldapClient->setAttrs( attrs );
00244 
00245       connect( ldapClient, SIGNAL( result( const KPIM::LdapObject& ) ),
00246            this, SLOT( slotAddResult( const KPIM::LdapObject& ) ) );
00247       connect( ldapClient, SIGNAL( done() ),
00248            this, SLOT( slotSearchDone() ) );
00249       connect( ldapClient, SIGNAL( error( const QString& ) ),
00250            this, SLOT( slotError( const QString& ) ) );
00251 
00252       mLdapClientList.append( ldapClient );
00253     }
00254 
00256     while ( mResultListView->header()->count() > 0 ) {
00257       mResultListView->removeColumn(0);
00258     }
00259 
00260     mResultListView->addColumn( i18n( "Full Name" ) );
00261     mResultListView->addColumn( i18n( "Email" ) );
00262     mResultListView->addColumn( i18n( "Home Number" ) );
00263     mResultListView->addColumn( i18n( "Work Number" ) );
00264     mResultListView->addColumn( i18n( "Mobile Number" ) );
00265     mResultListView->addColumn( i18n( "Fax Number" ) );
00266     mResultListView->addColumn( i18n( "Company" ) );
00267     mResultListView->addColumn( i18n( "Organization" ) );
00268     mResultListView->addColumn( i18n( "Street" ) );
00269     mResultListView->addColumn( i18n( "State" ) );
00270     mResultListView->addColumn( i18n( "Country" ) );
00271     mResultListView->addColumn( i18n( "Zip Code" ) );
00272     mResultListView->addColumn( i18n( "Postal Address" ) );
00273     mResultListView->addColumn( i18n( "City" ) );
00274     mResultListView->addColumn( i18n( "Department" ) );
00275     mResultListView->addColumn( i18n( "Description" ) );
00276     mResultListView->addColumn( i18n( "User ID" ) );
00277     mResultListView->addColumn( i18n( "Title" ) );
00278 
00279     mResultListView->clear();
00280   }
00281 }
00282 
00283 void LDAPSearchDialog::saveSettings()
00284 {
00285   KConfig config( "kaddressbookrc" );
00286   config.setGroup( "LDAPSearch" );
00287   config.writeEntry( "SearchType", mSearchType->currentItem() );
00288   config.sync();
00289 }
00290 
00291 void LDAPSearchDialog::cancelQuery()
00292 {
00293   for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
00294     client->cancelQuery();
00295   }
00296 }
00297 
00298 void LDAPSearchDialog::slotAddResult( const KPIM::LdapObject& obj )
00299 {
00300   new ContactListItem( mResultListView, obj.attrs );
00301 }
00302 
00303 void LDAPSearchDialog::slotSetScope( bool rec )
00304 {
00305   for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
00306     if ( rec )
00307       client->setScope( "sub" );
00308     else
00309       client->setScope( "one" );
00310   }
00311 }
00312 
00313 QString LDAPSearchDialog::makeFilter( const QString& query, const QString& attr,
00314                                       bool startsWith )
00315 {
00316   /* The reasoning behind this filter is:
00317    * If it's a person, or a distlist, show it, even if it doesn't have an email address.
00318    * If it's not a person, or a distlist, only show it if it has an email attribute.
00319    * This allows both resource accounts with an email address which are not a person and
00320    * person entries without an email address to show up, while still not showing things
00321    * like structural entries in the ldap tree. */
00322   QString result( "&(|(objectclass=person)(objectclass=groupofnames)(mail=*))(" );
00323   if( query.isEmpty() )
00324     // Return a filter that matches everything
00325     return result + "|(cn=*)(sn=*)" + ")";
00326 
00327   if ( attr == i18n( "Name" ) ) {
00328     result += startsWith ? "|(cn=%1*)(sn=%2*)" : "|(cn=*%1*)(sn=*%2*)";
00329     result = result.arg( query ).arg( query );
00330   } else {
00331     result += (startsWith ? "%1=%2*" : "%1=*%2*");
00332     if ( attr == i18n( "Email" ) ) {
00333       result = result.arg( "mail" ).arg( query );
00334     } else if ( attr == i18n( "Home Number" ) ) {
00335       result = result.arg( "homePhone" ).arg( query );
00336     } else if ( attr == i18n( "Work Number" ) ) {
00337       result = result.arg( "telephoneNumber" ).arg( query );
00338     } else {
00339       // Error?
00340       result = QString::null;
00341       return result;
00342     }
00343   }
00344   result += ")";
00345   return result;
00346 }
00347 
00348 void LDAPSearchDialog::slotStartSearch()
00349 {
00350   cancelQuery();
00351 
00352   QApplication::setOverrideCursor( Qt::waitCursor );
00353   mSearchButton->setText( i18n( "Stop" ) );
00354 
00355   disconnect( mSearchButton, SIGNAL( clicked() ),
00356               this, SLOT( slotStartSearch() ) );
00357   connect( mSearchButton, SIGNAL( clicked() ),
00358            this, SLOT( slotStopSearch() ) );
00359 
00360   bool startsWith = (mSearchType->currentItem() == 1);
00361 
00362   QString filter = makeFilter( mSearchEdit->text().stripWhiteSpace(), mFilterCombo->currentText(), startsWith );
00363 
00364    // loop in the list and run the KPIM::LdapClients
00365   mResultListView->clear();
00366   for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() )
00367     client->startQuery( filter );
00368 
00369   saveSettings();
00370 }
00371 
00372 void LDAPSearchDialog::slotStopSearch()
00373 {
00374   cancelQuery();
00375   slotSearchDone();
00376 }
00377 
00378 void LDAPSearchDialog::slotSearchDone()
00379 {
00380   // If there are no more active clients, we are done.
00381   for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
00382     if ( client->isActive() )
00383       return;
00384   }
00385 
00386   disconnect( mSearchButton, SIGNAL( clicked() ),
00387               this, SLOT( slotStopSearch() ) );
00388   connect( mSearchButton, SIGNAL( clicked() ),
00389            this, SLOT( slotStartSearch() ) );
00390 
00391   mSearchButton->setText( i18n( "&Search" ) );
00392   QApplication::restoreOverrideCursor();
00393 }
00394 
00395 void LDAPSearchDialog::slotError( const QString& error )
00396 {
00397   QApplication::restoreOverrideCursor();
00398   KMessageBox::error( this, error );
00399 }
00400 
00401 void LDAPSearchDialog::closeEvent( QCloseEvent* e )
00402 {
00403   slotStopSearch();
00404   e->accept();
00405 }
00406 
00411 QString LDAPSearchDialog::selectedEMails() const
00412 {
00413   QStringList result;
00414   ContactListItem* cli = static_cast<ContactListItem*>( mResultListView->firstChild() );
00415   while ( cli ) {
00416     if ( cli->isSelected() ) {
00417       QString email = asUtf8( cli->mAttrs[ "mail" ].first() ).stripWhiteSpace();
00418       if ( !email.isEmpty() ) {
00419         QString name = asUtf8( cli->mAttrs[ "cn" ].first() ).stripWhiteSpace();
00420         if ( name.isEmpty() ) {
00421           result << email;
00422         } else {
00423           result << name + " <" + email + ">";
00424         }
00425       }
00426     }
00427     cli = static_cast<ContactListItem*>( cli->nextSibling() );
00428   }
00429 
00430   return result.join( ", " );
00431 }
00432 
00433 void LDAPSearchDialog::slotHelp()
00434 {
00435   kapp->invokeHelp( "ldap-queries" );
00436 }
00437 
00438 void LDAPSearchDialog::slotUnselectAll()
00439 {
00440   mResultListView->selectAll( false );
00441 }
00442 
00443 void LDAPSearchDialog::slotSelectAll()
00444 {
00445   mResultListView->selectAll( true );
00446 }
00447 
00448 void LDAPSearchDialog::slotUser1()
00449 {
00450 
00451   KABC::Resource *resource = mCore->requestResource( this );
00452   if ( !resource ) return;
00453   KABLock::self( mAddressBook )->lock( resource );
00454 
00455   ContactListItem* cli = static_cast<ContactListItem*>( mResultListView->firstChild() );
00456   while ( cli ) {
00457     if ( cli->isSelected() ) {
00458       KABC::Addressee addr;
00459 
00460       // name
00461       addr.setNameFromString( asUtf8( cli->mAttrs["cn"].first() ) );
00462 
00463       // email
00464       KPIM::LdapAttrValue lst = cli->mAttrs["mail"];
00465       KPIM::LdapAttrValue::ConstIterator it = lst.begin();
00466       bool pref = true;
00467       if ( it != lst.end() ) {
00468         addr.insertEmail( asUtf8( *it ), pref );
00469         pref = false;
00470         ++it;
00471       }
00472 
00473       addr.setOrganization( asUtf8( cli->mAttrs[ "o" ].first() ) );
00474       if ( addr.organization().isEmpty() )
00475          addr.setOrganization( asUtf8( cli->mAttrs[ "Company" ].first() ) );
00476 
00477       addr.insertCustom("KADDRESSBOOK", "X-Department", asUtf8( cli->mAttrs[ "department" ].first() ) );
00478 
00479       // Address
00480       KABC::Address workAddr( KABC::Address::Work );
00481 
00482       workAddr.setStreet( asUtf8( cli->mAttrs[ "street" ].first()) );
00483       workAddr.setLocality( asUtf8( cli->mAttrs[ "l" ].first()) );
00484       workAddr.setRegion( asUtf8( cli->mAttrs[ "st" ].first()));
00485       workAddr.setPostalCode( asUtf8( cli->mAttrs[ "postalCode" ].first()) );
00486       workAddr.setCountry( asUtf8( cli->mAttrs[ "co" ].first()) );
00487 
00488       if ( !workAddr.isEmpty() )
00489         addr.insertAddress( workAddr );
00490 
00491       // phone
00492       KABC::PhoneNumber homeNr = asUtf8( cli->mAttrs[  "homePhone" ].first() );
00493       homeNr.setType( KABC::PhoneNumber::Home );
00494       addr.insertPhoneNumber( homeNr );
00495 
00496       KABC::PhoneNumber workNr = asUtf8( cli->mAttrs[  "telephoneNumber" ].first() );
00497       workNr.setType( KABC::PhoneNumber::Work );
00498       addr.insertPhoneNumber( workNr );
00499 
00500       KABC::PhoneNumber faxNr = asUtf8( cli->mAttrs[  "facsimileTelephoneNumber" ].first() );
00501       faxNr.setType( KABC::PhoneNumber::Fax );
00502       addr.insertPhoneNumber( faxNr );
00503 
00504       KABC::PhoneNumber cellNr = asUtf8( cli->mAttrs[  "mobile" ].first() );
00505       cellNr.setType( KABC::PhoneNumber::Cell );
00506       addr.insertPhoneNumber( cellNr );
00507 
00508       KABC::PhoneNumber pagerNr = asUtf8( cli->mAttrs[  "pager" ].first() );
00509       pagerNr.setType( KABC::PhoneNumber::Pager );
00510       addr.insertPhoneNumber( pagerNr );
00511 
00512       if ( mAddressBook ) {
00513         addr.setResource( resource );
00514         mAddressBook->insertAddressee( addr );
00515       }
00516     }
00517     cli = static_cast<ContactListItem*>( cli->nextSibling() );
00518   }
00519 
00520   KABLock::self( mAddressBook )->unlock( resource );
00521   emit addresseesAdded();
00522 }
00523 
00524 #include "ldapsearchdialog.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys