ldapsearchdialog.cpp
00001 /* ldapsearchdialogimpl.cpp - LDAP access 00002 * Copyright (C) 2002 Klarälvdalens 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 #include "ldapsearchdialog.h" 00022 #include "ldapclient.h" 00023 00024 #include <libemailfunctions/email.h> 00025 00026 #include <tqcheckbox.h> 00027 #include <tqgroupbox.h> 00028 #include <tqheader.h> 00029 #include <tqlabel.h> 00030 #include <tqlayout.h> 00031 #include <tqlistview.h> 00032 #include <tqpushbutton.h> 00033 00034 #include <tdeabc/addresslineedit.h> 00035 #include <tdeapplication.h> 00036 #include <kcombobox.h> 00037 #include <tdeconfig.h> 00038 #include <klineedit.h> 00039 #include <tdelocale.h> 00040 #include <tdemessagebox.h> 00041 00042 using namespace KPIM; 00043 00044 static TQString asUtf8( const TQByteArray &val ) 00045 { 00046 if ( val.isEmpty() ) 00047 return TQString(); 00048 00049 const char *data = val.data(); 00050 00051 //TQString::fromUtf8() bug workaround 00052 if ( data[ val.size() - 1 ] == '\0' ) 00053 return TQString::fromUtf8( data, val.size() - 1 ); 00054 else 00055 return TQString::fromUtf8( data, val.size() ); 00056 } 00057 00058 static TQString join( const KPIM::LdapAttrValue& lst, const TQString& sep ) 00059 { 00060 TQString res; 00061 bool alredy = false; 00062 for ( KPIM::LdapAttrValue::ConstIterator it = lst.begin(); it != lst.end(); ++it ) { 00063 if ( alredy ) 00064 res += sep; 00065 alredy = TRUE; 00066 res += asUtf8( *it ); 00067 } 00068 return res; 00069 } 00070 00071 static TQMap<TQString, TQString>& adrbookattr2ldap() 00072 { 00073 static TQMap<TQString, TQString> keys; 00074 00075 if ( keys.isEmpty() ) { 00076 keys[ i18n( "Title" ) ] = "title"; 00077 keys[ i18n( "Full Name" ) ] = "cn"; 00078 keys[ i18n( "Email" ) ] = "mail"; 00079 keys[ i18n( "Home Number" ) ] = "homePhone"; 00080 keys[ i18n( "Work Number" ) ] = "telephoneNumber"; 00081 keys[ i18n( "Mobile Number" ) ] = "mobile"; 00082 keys[ i18n( "Fax Number" ) ] = "facsimileTelephoneNumber"; 00083 keys[ i18n( "Pager" ) ] = "pager"; 00084 keys[ i18n( "Street") ] = "street"; 00085 keys[ i18n( "State" ) ] = "st"; 00086 keys[ i18n( "Country" ) ] = "co"; 00087 keys[ i18n( "City" ) ] = "l"; 00088 keys[ i18n( "Organization" ) ] = "o"; 00089 keys[ i18n( "Company" ) ] = "Company"; 00090 keys[ i18n( "Department" ) ] = "department"; 00091 keys[ i18n( "Zip Code" ) ] = "postalCode"; 00092 keys[ i18n( "Postal Address" ) ] = "postalAddress"; 00093 keys[ i18n( "Description" ) ] = "description"; 00094 keys[ i18n( "User ID" ) ] = "uid"; 00095 } 00096 return keys; 00097 } 00098 00099 namespace KPIM { 00100 00101 class ContactListItem : public TQListViewItem 00102 { 00103 public: 00104 ContactListItem( TQListView* parent, const KPIM::LdapAttrMap& attrs ) 00105 : TQListViewItem( parent ), mAttrs( attrs ) 00106 { 00107 const KPIM::LdapAttrValue &mailAttrs = attrs[ "mail" ]; 00108 if ( mailAttrs.isEmpty() ) { 00109 setSelectable( false ); 00110 setEnabled( false ); 00111 } 00112 } 00113 00114 KPIM::LdapAttrMap mAttrs; 00115 00116 virtual TQString text( int col ) const 00117 { 00118 // Look up a suitable attribute for column col 00119 const TQString colName = listView()->columnText( col ); 00120 const TQString ldapAttrName = adrbookattr2ldap()[ colName ]; 00121 return join( mAttrs[ ldapAttrName ], ", " ); 00122 } 00123 }; 00124 00125 } 00126 00127 LDAPSearchDialog::LDAPSearchDialog( TQWidget* parent, const char* name ) 00128 : KDialogBase( Plain, i18n( "Search for Addresses in Directory" ), Help | User1 | 00129 User2 | User3 | Cancel, Default, parent, name, false, true ) 00130 { 00131 setButtonCancel( KStdGuiItem::close() ); 00132 TQFrame *page = plainPage(); 00133 TQVBoxLayout *topLayout = new TQVBoxLayout( page, marginHint(), spacingHint() ); 00134 00135 TQGroupBox *groupBox = new TQGroupBox( i18n( "Search for Addresses in Directory" ), 00136 page ); 00137 groupBox->setFrameShape( TQGroupBox::Box ); 00138 groupBox->setFrameShadow( TQGroupBox::Sunken ); 00139 groupBox->setColumnLayout( 0, Qt::Vertical ); 00140 TQGridLayout *boxLayout = new TQGridLayout( groupBox->layout(), 2, 00141 5, spacingHint() ); 00142 boxLayout->setColStretch( 1, 1 ); 00143 00144 TQLabel *label = new TQLabel( i18n( "Search for:" ), groupBox ); 00145 boxLayout->addWidget( label, 0, 0 ); 00146 00147 mSearchEdit = new KLineEdit( groupBox ); 00148 boxLayout->addWidget( mSearchEdit, 0, 1 ); 00149 label->setBuddy( mSearchEdit ); 00150 00151 label = new TQLabel( i18n( "in" ), groupBox ); 00152 boxLayout->addWidget( label, 0, 2 ); 00153 00154 mFilterCombo = new KComboBox( groupBox ); 00155 mFilterCombo->insertItem( i18n( "Name" ) ); 00156 mFilterCombo->insertItem( i18n( "Email" ) ); 00157 mFilterCombo->insertItem( i18n( "Home Number" ) ); 00158 mFilterCombo->insertItem( i18n( "Work Number" ) ); 00159 boxLayout->addWidget( mFilterCombo, 0, 3 ); 00160 00161 TQSize buttonSize; 00162 mSearchButton = new TQPushButton( i18n( "Stop" ), groupBox ); 00163 buttonSize = mSearchButton->sizeHint(); 00164 mSearchButton->setText( i18n( "Search" ) ); 00165 if ( buttonSize.width() < mSearchButton->sizeHint().width() ) 00166 buttonSize = mSearchButton->sizeHint(); 00167 mSearchButton->setFixedWidth( buttonSize.width() ); 00168 00169 mSearchButton->setDefault( true ); 00170 boxLayout->addWidget( mSearchButton, 0, 4 ); 00171 00172 mRecursiveCheckbox = new TQCheckBox( i18n( "Recursive search" ), groupBox ); 00173 mRecursiveCheckbox->setChecked( true ); 00174 boxLayout->addMultiCellWidget( mRecursiveCheckbox, 1, 1, 0, 4 ); 00175 00176 mSearchType = new KComboBox( groupBox ); 00177 mSearchType->insertItem( i18n( "Contains" ) ); 00178 mSearchType->insertItem( i18n( "Starts With" ) ); 00179 boxLayout->addMultiCellWidget( mSearchType, 1, 1, 3, 4 ); 00180 00181 topLayout->addWidget( groupBox ); 00182 00183 mResultListView = new TQListView( page ); 00184 mResultListView->setSelectionMode( TQListView::Multi ); 00185 mResultListView->setAllColumnsShowFocus( true ); 00186 mResultListView->setShowSortIndicator( true ); 00187 topLayout->addWidget( mResultListView ); 00188 00189 resize( TQSize( 600, 400).expandedTo( minimumSizeHint() ) ); 00190 00191 setButtonText( User1, i18n( "Unselect All" ) ); 00192 setButtonText( User2, i18n( "Select All" ) ); 00193 setButtonText( User3, i18n( "Add Selected" ) ); 00194 00195 mNumHosts = 0; 00196 mIsOK = false; 00197 00198 connect( mRecursiveCheckbox, TQT_SIGNAL( toggled( bool ) ), 00199 this, TQT_SLOT( slotSetScope( bool ) ) ); 00200 connect( mSearchButton, TQT_SIGNAL( clicked() ), 00201 this, TQT_SLOT( slotStartSearch() ) ); 00202 00203 setTabOrder(mSearchEdit, mFilterCombo); 00204 setTabOrder(mFilterCombo, mSearchButton); 00205 mSearchEdit->setFocus(); 00206 00207 restoreSettings(); 00208 } 00209 00210 LDAPSearchDialog::~LDAPSearchDialog() 00211 { 00212 saveSettings(); 00213 } 00214 00215 void LDAPSearchDialog::restoreSettings() 00216 { 00217 // Create one KPIM::LdapClient per selected server and configure it. 00218 00219 // First clean the list to make sure it is empty at 00220 // the beginning of the process 00221 mLdapClientList.setAutoDelete( true ); 00222 mLdapClientList.clear(); 00223 00224 TDEConfig kabConfig( "kaddressbookrc" ); 00225 kabConfig.setGroup( "LDAPSearch" ); 00226 mSearchType->setCurrentItem( kabConfig.readNumEntry( "SearchType", 0 ) ); 00227 00228 // then read the config file and register all selected 00229 // server in the list 00230 TDEConfig* config = TDEABC::AddressLineEdit::config(); // singleton kabldaprc config object 00231 TDEConfigGroupSaver saver( config, "LDAP" ); 00232 mNumHosts = config->readUnsignedNumEntry( "NumSelectedHosts" ); 00233 if ( !mNumHosts ) { 00234 KMessageBox::error( this, i18n( "You must select a LDAP server before searching.\nYou can do this from the menu Settings/Configure KAddressBook." ) ); 00235 mIsOK = false; 00236 } else { 00237 mIsOK = true; 00238 for ( int j = 0; j < mNumHosts; ++j ) { 00239 KPIM::LdapServer ldapServer; 00240 00241 TQString host = config->readEntry( TQString( "SelectedHost%1" ).arg( j ), "" ); 00242 if ( !host.isEmpty() ) 00243 ldapServer.setHost( host ); 00244 00245 int port = config->readUnsignedNumEntry( TQString( "SelectedPort%1" ).arg( j ) ); 00246 if ( port ) 00247 ldapServer.setPort( port ); 00248 00249 TQString base = config->readEntry( TQString( "SelectedBase%1" ).arg( j ), "" ); 00250 if ( !base.isEmpty() ) 00251 ldapServer.setBaseDN( base ); 00252 00253 TQString bindDN = config->readEntry( TQString( "SelectedBind%1" ).arg( j ), "" ); 00254 if ( !bindDN.isEmpty() ) 00255 ldapServer.setBindDN( bindDN ); 00256 00257 TQString pwdBindDN = config->readEntry( TQString( "SelectedPwdBind%1" ).arg( j ), "" ); 00258 if ( !pwdBindDN.isEmpty() ) 00259 ldapServer.setPwdBindDN( pwdBindDN ); 00260 00261 KPIM::LdapClient* ldapClient = new KPIM::LdapClient( 0, TQT_TQOBJECT(this), "ldapclient" ); 00262 ldapClient->setServer( ldapServer ); 00263 00264 TQStringList attrs; 00265 00266 for ( TQMap<TQString,TQString>::Iterator it = adrbookattr2ldap().begin(); it != adrbookattr2ldap().end(); ++it ) 00267 attrs << *it; 00268 00269 ldapClient->setAttrs( attrs ); 00270 00271 connect( ldapClient, TQT_SIGNAL( result( const KPIM::LdapObject& ) ), 00272 this, TQT_SLOT( slotAddResult( const KPIM::LdapObject& ) ) ); 00273 connect( ldapClient, TQT_SIGNAL( done() ), 00274 this, TQT_SLOT( slotSearchDone() ) ); 00275 connect( ldapClient, TQT_SIGNAL( error( const TQString& ) ), 00276 this, TQT_SLOT( slotError( const TQString& ) ) ); 00277 00278 mLdapClientList.append( ldapClient ); 00279 } 00280 00282 while ( mResultListView->header()->count() > 0 ) { 00283 mResultListView->removeColumn(0); 00284 } 00285 00286 mResultListView->addColumn( i18n( "Full Name" ) ); 00287 mResultListView->addColumn( i18n( "Email" ) ); 00288 mResultListView->addColumn( i18n( "Home Number" ) ); 00289 mResultListView->addColumn( i18n( "Work Number" ) ); 00290 mResultListView->addColumn( i18n( "Mobile Number" ) ); 00291 mResultListView->addColumn( i18n( "Fax Number" ) ); 00292 mResultListView->addColumn( i18n( "Company" ) ); 00293 mResultListView->addColumn( i18n( "Organization" ) ); 00294 mResultListView->addColumn( i18n( "Street" ) ); 00295 mResultListView->addColumn( i18n( "State" ) ); 00296 mResultListView->addColumn( i18n( "Country" ) ); 00297 mResultListView->addColumn( i18n( "Zip Code" ) ); 00298 mResultListView->addColumn( i18n( "Postal Address" ) ); 00299 mResultListView->addColumn( i18n( "City" ) ); 00300 mResultListView->addColumn( i18n( "Department" ) ); 00301 mResultListView->addColumn( i18n( "Description" ) ); 00302 mResultListView->addColumn( i18n( "User ID" ) ); 00303 mResultListView->addColumn( i18n( "Title" ) ); 00304 00305 mResultListView->clear(); 00306 } 00307 } 00308 00309 void LDAPSearchDialog::saveSettings() 00310 { 00311 TDEConfig config( "kaddressbookrc" ); 00312 config.setGroup( "LDAPSearch" ); 00313 config.writeEntry( "SearchType", mSearchType->currentItem() ); 00314 config.sync(); 00315 } 00316 00317 void LDAPSearchDialog::cancelQuery() 00318 { 00319 for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { 00320 client->cancelQuery(); 00321 } 00322 } 00323 00324 void LDAPSearchDialog::slotAddResult( const KPIM::LdapObject& obj ) 00325 { 00326 new ContactListItem( mResultListView, obj.attrs ); 00327 } 00328 00329 void LDAPSearchDialog::slotSetScope( bool rec ) 00330 { 00331 for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { 00332 if ( rec ) 00333 client->setScope( "sub" ); 00334 else 00335 client->setScope( "one" ); 00336 } 00337 } 00338 00339 TQString LDAPSearchDialog::makeFilter( const TQString& query, const TQString& attr, 00340 bool startsWith ) 00341 { 00342 /* The reasoning behind this filter is: 00343 * If it's a person, or a distlist, show it, even if it doesn't have an email address. 00344 * If it's not a person, or a distlist, only show it if it has an email attribute. 00345 * This allows both resource accounts with an email address which are not a person and 00346 * person entries without an email address to show up, while still not showing things 00347 * like structural entries in the ldap tree. */ 00348 TQString result( "&(|(objectclass=person)(objectclass=groupofnames)(mail=*))(" ); 00349 if( query.isEmpty() ) 00350 // Return a filter that matches everything 00351 return result + "|(cn=*)(sn=*)" + ")"; 00352 00353 if ( attr == i18n( "Name" ) ) { 00354 result += startsWith ? "|(cn=%1*)(sn=%2*)" : "|(cn=*%1*)(sn=*%2*)"; 00355 result = result.arg( query ).arg( query ); 00356 } else { 00357 result += (startsWith ? "%1=%2*" : "%1=*%2*"); 00358 if ( attr == i18n( "Email" ) ) { 00359 result = result.arg( "mail" ).arg( query ); 00360 } else if ( attr == i18n( "Home Number" ) ) { 00361 result = result.arg( "homePhone" ).arg( query ); 00362 } else if ( attr == i18n( "Work Number" ) ) { 00363 result = result.arg( "telephoneNumber" ).arg( query ); 00364 } else { 00365 // Error? 00366 result = TQString(); 00367 return result; 00368 } 00369 } 00370 result += ")"; 00371 return result; 00372 } 00373 00374 void LDAPSearchDialog::slotStartSearch() 00375 { 00376 cancelQuery(); 00377 00378 TQApplication::setOverrideCursor( TQt::waitCursor ); 00379 mSearchButton->setText( i18n( "Stop" ) ); 00380 00381 disconnect( mSearchButton, TQT_SIGNAL( clicked() ), 00382 this, TQT_SLOT( slotStartSearch() ) ); 00383 connect( mSearchButton, TQT_SIGNAL( clicked() ), 00384 this, TQT_SLOT( slotStopSearch() ) ); 00385 00386 bool startsWith = (mSearchType->currentItem() == 1); 00387 00388 TQString filter = makeFilter( mSearchEdit->text().stripWhiteSpace(), mFilterCombo->currentText(), startsWith ); 00389 00390 // loop in the list and run the KPIM::LdapClients 00391 mResultListView->clear(); 00392 for( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { 00393 client->startQuery( filter ); 00394 } 00395 00396 saveSettings(); 00397 } 00398 00399 void LDAPSearchDialog::slotStopSearch() 00400 { 00401 cancelQuery(); 00402 slotSearchDone(); 00403 } 00404 00405 void LDAPSearchDialog::slotSearchDone() 00406 { 00407 // If there are no more active clients, we are done. 00408 for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) { 00409 if ( client->isActive() ) 00410 return; 00411 } 00412 00413 disconnect( mSearchButton, TQT_SIGNAL( clicked() ), 00414 this, TQT_SLOT( slotStopSearch() ) ); 00415 connect( mSearchButton, TQT_SIGNAL( clicked() ), 00416 this, TQT_SLOT( slotStartSearch() ) ); 00417 00418 mSearchButton->setText( i18n( "Search" ) ); 00419 TQApplication::restoreOverrideCursor(); 00420 } 00421 00422 void LDAPSearchDialog::slotError( const TQString& error ) 00423 { 00424 TQApplication::restoreOverrideCursor(); 00425 KMessageBox::error( this, error ); 00426 } 00427 00428 void LDAPSearchDialog::closeEvent( TQCloseEvent* e ) 00429 { 00430 slotStopSearch(); 00431 e->accept(); 00432 } 00433 00438 TQString LDAPSearchDialog::selectedEMails() const 00439 { 00440 TQStringList result; 00441 ContactListItem* cli = static_cast<ContactListItem*>( mResultListView->firstChild() ); 00442 while ( cli ) { 00443 if ( cli->isSelected() ) { 00444 TQString email = asUtf8( cli->mAttrs[ "mail" ].first() ).stripWhiteSpace(); 00445 if ( !email.isEmpty() ) { 00446 TQString name = asUtf8( cli->mAttrs[ "cn" ].first() ).stripWhiteSpace(); 00447 if ( name.isEmpty() ) { 00448 result << email; 00449 } else { 00450 result << KPIM::quoteNameIfNecessary( name ) + " <" + email + ">"; 00451 } 00452 } 00453 } 00454 cli = static_cast<ContactListItem*>( cli->nextSibling() ); 00455 } 00456 00457 return result.join( ", " ); 00458 } 00459 00460 void LDAPSearchDialog::slotHelp() 00461 { 00462 kapp->invokeHelp( "ldap-queries" ); 00463 } 00464 00465 void LDAPSearchDialog::slotUser1() 00466 { 00467 mResultListView->selectAll( false ); 00468 } 00469 00470 void LDAPSearchDialog::slotUser2() 00471 { 00472 mResultListView->selectAll( true ); 00473 } 00474 00475 void LDAPSearchDialog::slotUser3() 00476 { 00477 emit addresseesAdded(); 00478 } 00479 00480 #include "ldapsearchdialog.moc"