libtdepim

ldapsearchdialog.cpp
1 /* ldapsearchdialogimpl.cpp - LDAP access
2  * Copyright (C) 2002 Klarälvdalens Datakonsult AB
3  *
4  * Author: Steffen Hansen <hansen@kde.org>
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This file is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "ldapsearchdialog.h"
22 #include "ldapclient.h"
23 
24 #include <libemailfunctions/email.h>
25 
26 #include <tqcheckbox.h>
27 #include <tqgroupbox.h>
28 #include <tqheader.h>
29 #include <tqlabel.h>
30 #include <tqlayout.h>
31 #include <tqlistview.h>
32 #include <tqpushbutton.h>
33 
34 #include <tdeabc/addresslineedit.h>
35 #include <tdeapplication.h>
36 #include <kcombobox.h>
37 #include <tdeconfig.h>
38 #include <klineedit.h>
39 #include <tdelocale.h>
40 #include <tdemessagebox.h>
41 
42 using namespace KPIM;
43 
44 static TQString asUtf8( const TQByteArray &val )
45 {
46  if ( val.isEmpty() )
47  return TQString();
48 
49  const char *data = val.data();
50 
51  //TQString::fromUtf8() bug workaround
52  if ( data[ val.size() - 1 ] == '\0' )
53  return TQString::fromUtf8( data, val.size() - 1 );
54  else
55  return TQString::fromUtf8( data, val.size() );
56 }
57 
58 static TQString join( const KPIM::LdapAttrValue& lst, const TQString& sep )
59 {
60  TQString res;
61  bool alredy = false;
62  for ( KPIM::LdapAttrValue::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
63  if ( alredy )
64  res += sep;
65  alredy = TRUE;
66  res += asUtf8( *it );
67  }
68  return res;
69 }
70 
71 static TQMap<TQString, TQString>& adrbookattr2ldap()
72 {
73  static TQMap<TQString, TQString> keys;
74 
75  if ( keys.isEmpty() ) {
76  keys[ i18n( "Title" ) ] = "title";
77  keys[ i18n( "Full Name" ) ] = "cn";
78  keys[ i18n( "Email" ) ] = "mail";
79  keys[ i18n( "Home Number" ) ] = "homePhone";
80  keys[ i18n( "Work Number" ) ] = "telephoneNumber";
81  keys[ i18n( "Mobile Number" ) ] = "mobile";
82  keys[ i18n( "Fax Number" ) ] = "facsimileTelephoneNumber";
83  keys[ i18n( "Pager" ) ] = "pager";
84  keys[ i18n( "Street") ] = "street";
85  keys[ i18n( "State" ) ] = "st";
86  keys[ i18n( "Country" ) ] = "co";
87  keys[ i18n( "City" ) ] = "l";
88  keys[ i18n( "Organization" ) ] = "o";
89  keys[ i18n( "Company" ) ] = "Company";
90  keys[ i18n( "Department" ) ] = "department";
91  keys[ i18n( "Zip Code" ) ] = "postalCode";
92  keys[ i18n( "Postal Address" ) ] = "postalAddress";
93  keys[ i18n( "Description" ) ] = "description";
94  keys[ i18n( "User ID" ) ] = "uid";
95  }
96  return keys;
97 }
98 
99 namespace KPIM {
100 
101 class ContactListItem : public TQListViewItem
102 {
103  public:
104  ContactListItem( TQListView* parent, const KPIM::LdapAttrMap& attrs )
105  : TQListViewItem( parent ), mAttrs( attrs )
106  {
107  const KPIM::LdapAttrValue &mailAttrs = attrs[ "mail" ];
108  if ( mailAttrs.isEmpty() ) {
109  setSelectable( false );
110  setEnabled( false );
111  }
112  }
113 
114  KPIM::LdapAttrMap mAttrs;
115 
116  virtual TQString text( int col ) const
117  {
118  // Look up a suitable attribute for column col
119  const TQString colName = listView()->columnText( col );
120  const TQString ldapAttrName = adrbookattr2ldap()[ colName ];
121  return join( mAttrs[ ldapAttrName ], ", " );
122  }
123 };
124 
125 }
126 
127 LDAPSearchDialog::LDAPSearchDialog( TQWidget* parent, const char* name )
128  : KDialogBase( Plain, i18n( "Search for Addresses in Directory" ), Help | User1 |
129  User2 | User3 | Cancel, Default, parent, name, false, true )
130 {
131  setButtonCancel( KStdGuiItem::close() );
132  TQFrame *page = plainPage();
133  TQVBoxLayout *topLayout = new TQVBoxLayout( page, marginHint(), spacingHint() );
134 
135  TQGroupBox *groupBox = new TQGroupBox( i18n( "Search for Addresses in Directory" ),
136  page );
137  groupBox->setFrameShape( TQGroupBox::Box );
138  groupBox->setFrameShadow( TQGroupBox::Sunken );
139  groupBox->setColumnLayout( 0, TQt::Vertical );
140  TQGridLayout *boxLayout = new TQGridLayout( groupBox->layout(), 2,
141  5, spacingHint() );
142  boxLayout->setColStretch( 1, 1 );
143 
144  TQLabel *label = new TQLabel( i18n( "Search for:" ), groupBox );
145  boxLayout->addWidget( label, 0, 0 );
146 
147  mSearchEdit = new KLineEdit( groupBox );
148  boxLayout->addWidget( mSearchEdit, 0, 1 );
149  label->setBuddy( mSearchEdit );
150 
151  label = new TQLabel( i18n( "in" ), groupBox );
152  boxLayout->addWidget( label, 0, 2 );
153 
154  mFilterCombo = new KComboBox( groupBox );
155  mFilterCombo->insertItem( i18n( "Name" ) );
156  mFilterCombo->insertItem( i18n( "Email" ) );
157  mFilterCombo->insertItem( i18n( "Home Number" ) );
158  mFilterCombo->insertItem( i18n( "Work Number" ) );
159  boxLayout->addWidget( mFilterCombo, 0, 3 );
160 
161  TQSize buttonSize;
162  mSearchButton = new TQPushButton( i18n( "Stop" ), groupBox );
163  buttonSize = mSearchButton->sizeHint();
164  mSearchButton->setText( i18n( "Search" ) );
165  if ( buttonSize.width() < mSearchButton->sizeHint().width() )
166  buttonSize = mSearchButton->sizeHint();
167  mSearchButton->setFixedWidth( buttonSize.width() );
168 
169  mSearchButton->setDefault( true );
170  boxLayout->addWidget( mSearchButton, 0, 4 );
171 
172  mRecursiveCheckbox = new TQCheckBox( i18n( "Recursive search" ), groupBox );
173  mRecursiveCheckbox->setChecked( true );
174  boxLayout->addMultiCellWidget( mRecursiveCheckbox, 1, 1, 0, 4 );
175 
176  mSearchType = new KComboBox( groupBox );
177  mSearchType->insertItem( i18n( "Contains" ) );
178  mSearchType->insertItem( i18n( "Starts With" ) );
179  boxLayout->addMultiCellWidget( mSearchType, 1, 1, 3, 4 );
180 
181  topLayout->addWidget( groupBox );
182 
183  mResultListView = new TQListView( page );
184  mResultListView->setSelectionMode( TQListView::Multi );
185  mResultListView->setAllColumnsShowFocus( true );
186  mResultListView->setShowSortIndicator( true );
187  topLayout->addWidget( mResultListView );
188 
189  resize( TQSize( 600, 400).expandedTo( minimumSizeHint() ) );
190 
191  setButtonText( User1, i18n( "Unselect All" ) );
192  setButtonText( User2, i18n( "Select All" ) );
193  setButtonText( User3, i18n( "Add Selected" ) );
194 
195  mNumHosts = 0;
196  mIsOK = false;
197 
198  connect( mRecursiveCheckbox, TQ_SIGNAL( toggled( bool ) ),
199  this, TQ_SLOT( slotSetScope( bool ) ) );
200  connect( mSearchButton, TQ_SIGNAL( clicked() ),
201  this, TQ_SLOT( slotStartSearch() ) );
202 
203  setTabOrder(mSearchEdit, mFilterCombo);
204  setTabOrder(mFilterCombo, mSearchButton);
205  mSearchEdit->setFocus();
206 
207  restoreSettings();
208 }
209 
210 LDAPSearchDialog::~LDAPSearchDialog()
211 {
212  saveSettings();
213 }
214 
215 void LDAPSearchDialog::restoreSettings()
216 {
217  // Create one KPIM::LdapClient per selected server and configure it.
218 
219  // First clean the list to make sure it is empty at
220  // the beginning of the process
221  mLdapClientList.setAutoDelete( true );
222  mLdapClientList.clear();
223 
224  TDEConfig kabConfig( "kaddressbookrc" );
225  kabConfig.setGroup( "LDAPSearch" );
226  mSearchType->setCurrentItem( kabConfig.readNumEntry( "SearchType", 0 ) );
227 
228  // then read the config file and register all selected
229  // server in the list
230  TDEConfig* config = TDEABC::AddressLineEdit::config(); // singleton kabldaprc config object
231  TDEConfigGroupSaver saver( config, "LDAP" );
232  mNumHosts = config->readUnsignedNumEntry( "NumSelectedHosts" );
233  if ( !mNumHosts ) {
234  KMessageBox::error( this, i18n( "You must select a LDAP server before searching.\nYou can do this from the menu Settings/Configure KAddressBook." ) );
235  mIsOK = false;
236  } else {
237  mIsOK = true;
238  for ( int j = 0; j < mNumHosts; ++j ) {
239  KPIM::LdapServer ldapServer;
240 
241  TQString host = config->readEntry( TQString( "SelectedHost%1" ).arg( j ), "" );
242  if ( !host.isEmpty() )
243  ldapServer.setHost( host );
244 
245  int port = config->readUnsignedNumEntry( TQString( "SelectedPort%1" ).arg( j ) );
246  if ( port )
247  ldapServer.setPort( port );
248 
249  TQString base = config->readEntry( TQString( "SelectedBase%1" ).arg( j ), "" );
250  if ( !base.isEmpty() )
251  ldapServer.setBaseDN( base );
252 
253  TQString bindDN = config->readEntry( TQString( "SelectedBind%1" ).arg( j ), "" );
254  if ( !bindDN.isEmpty() )
255  ldapServer.setBindDN( bindDN );
256 
257  TQString pwdBindDN = config->readEntry( TQString( "SelectedPwdBind%1" ).arg( j ), "" );
258  if ( !pwdBindDN.isEmpty() )
259  ldapServer.setPwdBindDN( pwdBindDN );
260 
261  KPIM::LdapClient* ldapClient = new KPIM::LdapClient( 0, this, "ldapclient" );
262  ldapClient->setServer( ldapServer );
263 
264  TQStringList attrs;
265 
266  for ( TQMap<TQString,TQString>::Iterator it = adrbookattr2ldap().begin(); it != adrbookattr2ldap().end(); ++it )
267  attrs << *it;
268 
269  ldapClient->setAttrs( attrs );
270 
271  connect( ldapClient, TQ_SIGNAL( result( const KPIM::LdapObject& ) ),
272  this, TQ_SLOT( slotAddResult( const KPIM::LdapObject& ) ) );
273  connect( ldapClient, TQ_SIGNAL( done() ),
274  this, TQ_SLOT( slotSearchDone() ) );
275  connect( ldapClient, TQ_SIGNAL( error( const TQString& ) ),
276  this, TQ_SLOT( slotError( const TQString& ) ) );
277 
278  mLdapClientList.append( ldapClient );
279  }
280 
282  while ( mResultListView->header()->count() > 0 ) {
283  mResultListView->removeColumn(0);
284  }
285 
286  mResultListView->addColumn( i18n( "Full Name" ) );
287  mResultListView->addColumn( i18n( "Email" ) );
288  mResultListView->addColumn( i18n( "Home Number" ) );
289  mResultListView->addColumn( i18n( "Work Number" ) );
290  mResultListView->addColumn( i18n( "Mobile Number" ) );
291  mResultListView->addColumn( i18n( "Fax Number" ) );
292  mResultListView->addColumn( i18n( "Company" ) );
293  mResultListView->addColumn( i18n( "Organization" ) );
294  mResultListView->addColumn( i18n( "Street" ) );
295  mResultListView->addColumn( i18n( "State" ) );
296  mResultListView->addColumn( i18n( "Country" ) );
297  mResultListView->addColumn( i18n( "Zip Code" ) );
298  mResultListView->addColumn( i18n( "Postal Address" ) );
299  mResultListView->addColumn( i18n( "City" ) );
300  mResultListView->addColumn( i18n( "Department" ) );
301  mResultListView->addColumn( i18n( "Description" ) );
302  mResultListView->addColumn( i18n( "User ID" ) );
303  mResultListView->addColumn( i18n( "Title" ) );
304 
305  mResultListView->clear();
306  }
307 }
308 
309 void LDAPSearchDialog::saveSettings()
310 {
311  TDEConfig config( "kaddressbookrc" );
312  config.setGroup( "LDAPSearch" );
313  config.writeEntry( "SearchType", mSearchType->currentItem() );
314  config.sync();
315 }
316 
317 void LDAPSearchDialog::cancelQuery()
318 {
319  for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
320  client->cancelQuery();
321  }
322 }
323 
324 void LDAPSearchDialog::slotAddResult( const KPIM::LdapObject& obj )
325 {
326  new ContactListItem( mResultListView, obj.attrs );
327 }
328 
329 void LDAPSearchDialog::slotSetScope( bool rec )
330 {
331  for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
332  if ( rec )
333  client->setScope( "sub" );
334  else
335  client->setScope( "one" );
336  }
337 }
338 
339 TQString LDAPSearchDialog::makeFilter( const TQString& query, const TQString& attr,
340  bool startsWith )
341 {
342  /* The reasoning behind this filter is:
343  * If it's a person, or a distlist, show it, even if it doesn't have an email address.
344  * If it's not a person, or a distlist, only show it if it has an email attribute.
345  * This allows both resource accounts with an email address which are not a person and
346  * person entries without an email address to show up, while still not showing things
347  * like structural entries in the ldap tree. */
348  TQString result( "&(|(objectclass=person)(objectclass=groupofnames)(mail=*))(" );
349  if( query.isEmpty() )
350  // Return a filter that matches everything
351  return result + "|(cn=*)(sn=*)" + ")";
352 
353  if ( attr == i18n( "Name" ) ) {
354  result += startsWith ? "|(cn=%1*)(sn=%2*)" : "|(cn=*%1*)(sn=*%2*)";
355  result = result.arg( query ).arg( query );
356  } else {
357  result += (startsWith ? "%1=%2*" : "%1=*%2*");
358  if ( attr == i18n( "Email" ) ) {
359  result = result.arg( "mail" ).arg( query );
360  } else if ( attr == i18n( "Home Number" ) ) {
361  result = result.arg( "homePhone" ).arg( query );
362  } else if ( attr == i18n( "Work Number" ) ) {
363  result = result.arg( "telephoneNumber" ).arg( query );
364  } else {
365  // Error?
366  result = TQString();
367  return result;
368  }
369  }
370  result += ")";
371  return result;
372 }
373 
374 void LDAPSearchDialog::slotStartSearch()
375 {
376  cancelQuery();
377 
378  TQApplication::setOverrideCursor( TQt::waitCursor );
379  mSearchButton->setText( i18n( "Stop" ) );
380 
381  disconnect( mSearchButton, TQ_SIGNAL( clicked() ),
382  this, TQ_SLOT( slotStartSearch() ) );
383  connect( mSearchButton, TQ_SIGNAL( clicked() ),
384  this, TQ_SLOT( slotStopSearch() ) );
385 
386  bool startsWith = (mSearchType->currentItem() == 1);
387 
388  TQString filter = makeFilter( mSearchEdit->text().stripWhiteSpace(), mFilterCombo->currentText(), startsWith );
389 
390  // loop in the list and run the KPIM::LdapClients
391  mResultListView->clear();
392  for( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
393  client->startQuery( filter );
394  }
395 
396  saveSettings();
397 }
398 
399 void LDAPSearchDialog::slotStopSearch()
400 {
401  cancelQuery();
402  slotSearchDone();
403 }
404 
405 void LDAPSearchDialog::slotSearchDone()
406 {
407  // If there are no more active clients, we are done.
408  for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
409  if ( client->isActive() )
410  return;
411  }
412 
413  disconnect( mSearchButton, TQ_SIGNAL( clicked() ),
414  this, TQ_SLOT( slotStopSearch() ) );
415  connect( mSearchButton, TQ_SIGNAL( clicked() ),
416  this, TQ_SLOT( slotStartSearch() ) );
417 
418  mSearchButton->setText( i18n( "Search" ) );
419  TQApplication::restoreOverrideCursor();
420 }
421 
422 void LDAPSearchDialog::slotError( const TQString& error )
423 {
424  TQApplication::restoreOverrideCursor();
425  KMessageBox::error( this, error );
426 }
427 
428 void LDAPSearchDialog::closeEvent( TQCloseEvent* e )
429 {
430  slotStopSearch();
431  e->accept();
432 }
433 
438 TQString LDAPSearchDialog::selectedEMails() const
439 {
440  TQStringList result;
441  ContactListItem* cli = static_cast<ContactListItem*>( mResultListView->firstChild() );
442  while ( cli ) {
443  if ( cli->isSelected() ) {
444  TQString email = asUtf8( cli->mAttrs[ "mail" ].first() ).stripWhiteSpace();
445  if ( !email.isEmpty() ) {
446  TQString name = asUtf8( cli->mAttrs[ "cn" ].first() ).stripWhiteSpace();
447  if ( name.isEmpty() ) {
448  result << email;
449  } else {
450  result << KPIM::quoteNameIfNecessary( name ) + " <" + email + ">";
451  }
452  }
453  }
454  cli = static_cast<ContactListItem*>( cli->nextSibling() );
455  }
456 
457  return result.join( ", " );
458 }
459 
460 void LDAPSearchDialog::slotHelp()
461 {
462  kapp->invokeHelp( "ldap-queries" );
463 }
464 
465 void LDAPSearchDialog::slotUser1()
466 {
467  mResultListView->selectAll( false );
468 }
469 
470 void LDAPSearchDialog::slotUser2()
471 {
472  mResultListView->selectAll( true );
473 }
474 
475 void LDAPSearchDialog::slotUser3()
476 {
477  emit addresseesAdded();
478 }
479 
480 #include "ldapsearchdialog.moc"
This class is internal.
Definition: ldapclient.h:143
void setAttrs(const TQStringList &attrs)
Definition: ldapclient.cpp:93
This class is internal.
Definition: ldapclient.h:106
TDEPIM classes for drag and drop of mails.