1/*
2 Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
3
4 Based on MailTransport code by:
5 Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
6 Copyright (c) 2007 KovoKs <kovoks@kovoks.nl>
7
8 Based on KMail code by:
9 Copyright (c) 2001-2002 Michael Haeckel <haeckel@kde.org>
10
11 This library is free software; you can redistribute it and/or modify it
12 under the terms of the GNU Library General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
15
16 This library is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
19 License for more details.
20
21 You should have received a copy of the GNU Library General Public License
22 along with this library; see the file COPYING.LIB. If not, write to the
23 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 02110-1301, USA.
25*/
26
27#include "smtpconfigwidget.h"
28#include "transportconfigwidget_p.h"
29#include "transport.h"
30#include "transportmanager.h"
31#include "servertest.h"
32#include "mailtransport_defs.h"
33
34#ifndef KDEPIM_MOBILE_UI
35#include "ui_smtpsettings_desktop.h"
36#else
37#include "ui_smtpsettings_mobile.h"
38#endif
39
40#include <QAbstractButton>
41#include <QButtonGroup>
42
43#include <KProtocolInfo>
44#include <KDebug>
45#include <KMessageBox>
46
47namespace {
48
49// TODO: is this really necessary?
50class BusyCursorHelper : public QObject
51{
52 public:
53 inline BusyCursorHelper( QObject *parent ) : QObject( parent )
54 {
55#ifndef QT_NO_CURSOR
56 qApp->setOverrideCursor( Qt::BusyCursor );
57#endif
58 }
59
60 inline ~BusyCursorHelper()
61 {
62#ifndef QT_NO_CURSOR
63 qApp->restoreOverrideCursor();
64#endif
65 }
66};
67
68}
69
70using namespace MailTransport;
71
72class MailTransport::SMTPConfigWidgetPrivate : public TransportConfigWidgetPrivate
73{
74 public:
75 ::Ui::SMTPSettings ui;
76
77 ServerTest *serverTest;
78 QButtonGroup *encryptionGroup;
79
80 // detected authentication capabilities
81 QList<int> noEncCapa, sslCapa, tlsCapa;
82
83 bool serverTestFailed;
84
85 static void addAuthenticationItem( KComboBox *combo,
86 int authenticationType )
87 {
88 combo->addItem( Transport::authenticationTypeString( authenticationType ),
89 QVariant( authenticationType ) );
90 }
91
92 void resetAuthCapabilities()
93 {
94 noEncCapa.clear();
95 noEncCapa << Transport::EnumAuthenticationType::LOGIN
96 << Transport::EnumAuthenticationType::PLAIN
97 << Transport::EnumAuthenticationType::CRAM_MD5
98 << Transport::EnumAuthenticationType::DIGEST_MD5
99 << Transport::EnumAuthenticationType::NTLM
100 << Transport::EnumAuthenticationType::GSSAPI;
101 sslCapa = tlsCapa = noEncCapa;
102 updateAuthCapbilities();
103
104 }
105
106 void updateAuthCapbilities()
107 {
108 if ( serverTestFailed ) {
109 return;
110 }
111
112 QList<int> capa = noEncCapa;
113 if ( ui.ssl->isChecked() ) {
114 capa = sslCapa;
115 } else if ( ui.tls->isChecked() ) {
116 capa = tlsCapa;
117 }
118
119 ui.authCombo->clear();
120 foreach ( int authType, capa ) {
121 addAuthenticationItem( ui.authCombo, authType );
122 }
123
124 if ( transport->isValid() ) {
125 const int idx = ui.authCombo->findData( transport->authenticationType() );
126
127 if ( idx != -1 ) {
128 ui.authCombo->setCurrentIndex( idx );
129 }
130 }
131
132 if ( capa.count() == 0 ) {
133 ui.noAuthPossible->setVisible( true );
134 ui.kcfg_requiresAuthentication->setChecked( false );
135 ui.kcfg_requiresAuthentication->setEnabled( false );
136 ui.kcfg_requiresAuthentication->setVisible( false );
137 ui.authCombo->setEnabled( false );
138 ui.authLabel->setEnabled( false );
139 } else {
140 ui.noAuthPossible->setVisible( false );
141 ui.kcfg_requiresAuthentication->setEnabled( true );
142 ui.kcfg_requiresAuthentication->setVisible( true );
143 ui.authCombo->setEnabled( true );
144 ui.authLabel->setEnabled( true );
145 }
146 }
147};
148
149SMTPConfigWidget::SMTPConfigWidget( Transport *transport, QWidget *parent )
150 : TransportConfigWidget( *new SMTPConfigWidgetPrivate, transport, parent )
151{
152 init();
153}
154
155SMTPConfigWidget::SMTPConfigWidget( SMTPConfigWidgetPrivate &dd,
156 Transport *transport, QWidget *parent )
157 : TransportConfigWidget( dd, transport, parent )
158{
159 init();
160}
161
162static void checkHighestEnabledButton( QButtonGroup *group )
163{
164 Q_ASSERT( group );
165
166 for ( int i = group->buttons().count() - 1; i >= 0; --i ) {
167 QAbstractButton *b = group->buttons().at( i );
168 if ( b && b->isEnabled() ) {
169 b->animateClick();
170 return;
171 }
172 }
173}
174
175void SMTPConfigWidget::init()
176{
177 Q_D( SMTPConfigWidget );
178 d->serverTest = 0;
179
180 connect( TransportManager::self(), SIGNAL(passwordsChanged()),
181 SLOT(passwordsLoaded()) );
182
183 d->serverTestFailed = false;
184
185 d->ui.setupUi( this );
186 d->manager->addWidget( this ); // otherwise it doesn't find out about these widgets
187 d->manager->updateWidgets();
188
189 d->encryptionGroup = new QButtonGroup( this );
190 d->encryptionGroup->addButton( d->ui.none, Transport::EnumEncryption::None );
191 d->encryptionGroup->addButton( d->ui.ssl, Transport::EnumEncryption::SSL );
192 d->encryptionGroup->addButton( d->ui.tls, Transport::EnumEncryption::TLS );
193
194 d->resetAuthCapabilities();
195
196 if ( KProtocolInfo::capabilities( SMTP_PROTOCOL ).contains( QLatin1String( "SASL" ) ) == 0 ) {
197 d->ui.authCombo->removeItem( d->ui.authCombo->findData(
198 Transport::EnumAuthenticationType::NTLM ) );
199 d->ui.authCombo->removeItem( d->ui.authCombo->findData(
200 Transport::EnumAuthenticationType::GSSAPI ) );
201 }
202
203 connect( d->ui.checkCapabilities, SIGNAL(clicked()),
204 SLOT(checkSmtpCapabilities()) );
205 connect( d->ui.kcfg_host, SIGNAL(textChanged(QString)),
206 SLOT(hostNameChanged(QString)) );
207 connect( d->encryptionGroup, SIGNAL(buttonClicked(int)),
208 SLOT(encryptionChanged(int)) );
209 connect( d->ui.kcfg_requiresAuthentication, SIGNAL(toggled(bool)),
210 SLOT(ensureValidAuthSelection()) );
211
212 if ( !d->transport->isValid() ) {
213 checkHighestEnabledButton( d->encryptionGroup );
214 }
215
216 // load the password
217 d->transport->updatePasswordState();
218 if ( d->transport->isComplete() ) {
219 d->ui.password->setText( d->transport->password() );
220 } else {
221 if ( d->transport->requiresAuthentication() ) {
222 TransportManager::self()->loadPasswordsAsync();
223 }
224 }
225
226 hostNameChanged( d->transport->host() );
227
228#ifdef KDEPIM_MOBILE_UI
229 d->ui.smtpSettingsGroupBox->hide();
230#endif
231}
232
233void SMTPConfigWidget::checkSmtpCapabilities()
234{
235 Q_D( SMTPConfigWidget );
236
237 d->serverTest = new ServerTest( this );
238 d->serverTest->setProtocol( SMTP_PROTOCOL );
239 d->serverTest->setServer( d->ui.kcfg_host->text().trimmed() );
240 if ( d->ui.kcfg_specifyHostname->isChecked() ) {
241 d->serverTest->setFakeHostname( d->ui.kcfg_localHostname->text() );
242 }
243 QAbstractButton *encryptionChecked = d->encryptionGroup->checkedButton();
244 if (encryptionChecked == d->ui.none) {
245 d->serverTest->setPort( Transport::EnumEncryption::None, d->ui.kcfg_port->value());
246 } else if (encryptionChecked == d->ui.ssl) {
247 d->serverTest->setPort( Transport::EnumEncryption::SSL, d->ui.kcfg_port->value());
248 }
249 d->serverTest->setProgressBar( d->ui.checkCapabilitiesProgress );
250 d->ui.checkCapabilitiesStack->setCurrentIndex( 1 );
251 BusyCursorHelper *busyCursorHelper = new BusyCursorHelper( d->serverTest );
252
253 connect( d->serverTest, SIGNAL(finished(QList<int>)),
254 SLOT(slotFinished(QList<int>)));
255 connect( d->serverTest, SIGNAL(finished(QList<int>)),
256 busyCursorHelper, SLOT(deleteLater()) );
257 d->ui.checkCapabilities->setEnabled( false );
258 d->serverTest->start();
259 d->serverTestFailed = false;
260}
261
262void SMTPConfigWidget::apply()
263{
264 Q_D( SMTPConfigWidget );
265 Q_ASSERT( d->manager );
266 d->manager->updateSettings();
267 d->transport->setPassword( d->ui.password->text() );
268
269 KConfigGroup group( d->transport->config(), d->transport->currentGroup() );
270 const int index = d->ui.authCombo->currentIndex();
271 if ( index >= 0 ) {
272 group.writeEntry( "authtype", d->ui.authCombo->itemData( index ).toInt() );
273 }
274
275 TransportConfigWidget::apply();
276}
277
278void SMTPConfigWidget::passwordsLoaded()
279{
280 Q_D( SMTPConfigWidget );
281
282 // Load the password from the original to our cloned copy
283 d->transport->updatePasswordState();
284
285 if ( d->ui.password->text().isEmpty() ) {
286 d->ui.password->setText( d->transport->password() );
287 }
288}
289
290// TODO rename
291void SMTPConfigWidget::slotFinished( QList<int> results )
292{
293 Q_D( SMTPConfigWidget );
294
295 d->ui.checkCapabilitiesStack->setCurrentIndex( 0 );
296
297 d->ui.checkCapabilities->setEnabled( true );
298 d->serverTest->deleteLater();
299
300 // If the servertest did not find any useable authentication modes, assume the
301 // connection failed and don't disable any of the radioboxes.
302 if ( results.isEmpty() ) {
303 KMessageBox::error(this, i18n("Failed to check capabilities. Please verify port and authentication mode."), i18n("Check Capabilities Failed"));
304 d->serverTestFailed = true;
305 d->serverTest->deleteLater();
306 return;
307 }
308
309 // encryption method
310 d->ui.none->setEnabled( results.contains( Transport::EnumEncryption::None ) );
311 d->ui.ssl->setEnabled( results.contains( Transport::EnumEncryption::SSL ) );
312 d->ui.tls->setEnabled( results.contains( Transport::EnumEncryption::TLS ) );
313 checkHighestEnabledButton( d->encryptionGroup );
314
315 d->noEncCapa = d->serverTest->normalProtocols();
316 if ( d->ui.tls->isEnabled() ) {
317 d->tlsCapa = d->serverTest->tlsProtocols();
318 } else {
319 d->tlsCapa.clear();
320 }
321 d->sslCapa = d->serverTest->secureProtocols();
322 d->updateAuthCapbilities();
323 //Show correct port from capabilities.
324 if (d->ui.ssl->isEnabled()) {
325 const int portValue = d->serverTest->port(Transport::EnumEncryption::SSL);
326 d->ui.kcfg_port->setValue(portValue == -1 ? SMTPS_PORT : portValue);
327 } else if (d->ui.none->isEnabled()) {
328 const int portValue = d->serverTest->port(Transport::EnumEncryption::None);
329 d->ui.kcfg_port->setValue(portValue == -1 ? SMTP_PORT : portValue);
330 }
331 d->serverTest->deleteLater();
332}
333
334void SMTPConfigWidget::hostNameChanged( const QString &text )
335{
336 // TODO: really? is this done at every change? wtf
337
338 Q_D( SMTPConfigWidget );
339
340 // sanitize hostname...
341 int pos = d->ui.kcfg_host->cursorPosition();
342 d->ui.kcfg_host->blockSignals( true );
343 d->ui.kcfg_host->setText( text.trimmed() );
344 d->ui.kcfg_host->blockSignals( false );
345 d->ui.kcfg_host->setCursorPosition( pos );
346
347 d->resetAuthCapabilities();
348 for ( int i = 0; d->encryptionGroup && i < d->encryptionGroup->buttons().count(); i++ ) {
349 d->encryptionGroup->buttons().at( i )->setEnabled( true );
350 }
351}
352
353void SMTPConfigWidget::ensureValidAuthSelection()
354{
355 Q_D( SMTPConfigWidget );
356
357 // adjust available authentication methods
358 d->updateAuthCapbilities();
359}
360
361void SMTPConfigWidget::encryptionChanged( int enc )
362{
363 Q_D( SMTPConfigWidget );
364 kDebug() << enc;
365
366 // adjust port
367 if ( enc == Transport::EnumEncryption::SSL ) {
368 if ( d->ui.kcfg_port->value() == SMTP_PORT ) {
369 d->ui.kcfg_port->setValue( SMTPS_PORT );
370 }
371 } else {
372 if ( d->ui.kcfg_port->value() == SMTPS_PORT ) {
373 d->ui.kcfg_port->setValue( SMTP_PORT );
374 }
375 }
376
377 ensureValidAuthSelection();
378}
379
380