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
42using namespace KPIM;
43
44static 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
58static 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
71static 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
99namespace KPIM {
100
101class 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
127LDAPSearchDialog::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
210LDAPSearchDialog::~LDAPSearchDialog()
211{
212 saveSettings();
213}
214
215void 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
309void LDAPSearchDialog::saveSettings()
310{
311 TDEConfig config( "kaddressbookrc" );
312 config.setGroup( "LDAPSearch" );
313 config.writeEntry( "SearchType", mSearchType->currentItem() );
314 config.sync();
315}
316
317void LDAPSearchDialog::cancelQuery()
318{
319 for ( KPIM::LdapClient* client = mLdapClientList.first(); client; client = mLdapClientList.next() ) {
320 client->cancelQuery();
321 }
322}
323
324void LDAPSearchDialog::slotAddResult( const KPIM::LdapObject& obj )
325{
326 new ContactListItem( mResultListView, obj.attrs );
327}
328
329void 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
339TQString 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
374void 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
399void LDAPSearchDialog::slotStopSearch()
400{
401 cancelQuery();
402 slotSearchDone();
403}
404
405void 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
422void LDAPSearchDialog::slotError( const TQString& error )
423{
424 TQApplication::restoreOverrideCursor();
425 KMessageBox::error( this, error );
426}
427
428void LDAPSearchDialog::closeEvent( TQCloseEvent* e )
429{
430 slotStopSearch();
431 e->accept();
432}
433
438TQString 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
460void LDAPSearchDialog::slotHelp()
461{
462 tdeApp->invokeHelp( "ldap-queries" );
463}
464
465void LDAPSearchDialog::slotUser1()
466{
467 mResultListView->selectAll( false );
468}
469
470void LDAPSearchDialog::slotUser2()
471{
472 mResultListView->selectAll( true );
473}
474
475void 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.