MailTransport

smtpconfigwidget.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
3
4 Based on MailTransport code by:
5 SPDX-FileCopyrightText: 2006-2007 Volker Krause <vkrause@kde.org>
6 SPDX-FileCopyrightText: 2007 KovoKs <kovoks@kovoks.nl>
7
8 Based on KMail code by:
9 SPDX-FileCopyrightText: 2001-2002 Michael Haeckel <haeckel@kde.org>
10
11 SPDX-License-Identifier: LGPL-2.0-or-later
12*/
13
14#include "smtpconfigwidget.h"
15#include "ui_smtpsettings.h"
16
17#include "mailtransport_defs.h"
18#include "mailtransportplugin_smtp_debug.h"
19#include "servertest.h"
20#include "transport.h"
21#include "transportmanager.h"
22#include "widgets/transportconfigwidget_p.h"
23
24#include <QAbstractButton>
25#include <QButtonGroup>
26
27#include "mailtransport_debug.h"
28#include <KAuthorized>
29#include <KLocalizedString>
30#include <KMessageBox>
31#include <KProtocolInfo>
32
33using namespace MailTransport;
34
35class MailTransport::SMTPConfigWidgetPrivate : public TransportConfigWidgetPrivate
36{
37public:
38 ::Ui::SMTPSettings ui;
39
40 ServerTest *serverTest = nullptr;
41 QButtonGroup *encryptionGroup = nullptr;
42
43 // detected authentication capabilities
44 QList<int> noEncCapa, sslCapa, tlsCapa;
45
46 bool serverTestFailed;
47
48 static void addAuthenticationItem(QComboBox *combo, int authenticationType)
49 {
50 combo->addItem(Transport::authenticationTypeString(authenticationType), QVariant(authenticationType));
51 }
52
53 void resetAuthCapabilities()
54 {
55 noEncCapa.clear();
56 noEncCapa << Transport::EnumAuthenticationType::LOGIN << Transport::EnumAuthenticationType::PLAIN << Transport::EnumAuthenticationType::CRAM_MD5
57 << Transport::EnumAuthenticationType::DIGEST_MD5 << Transport::EnumAuthenticationType::NTLM << Transport::EnumAuthenticationType::GSSAPI
58 << Transport::EnumAuthenticationType::XOAUTH2;
59 sslCapa = tlsCapa = noEncCapa;
60 updateAuthCapbilities();
61 }
62
63 void enablePasswordLine()
64 {
65 ui.password->setEnabled(ui.kcfg_storePassword->isChecked());
66 }
67
68 void updateAuthCapbilities()
69 {
70 if (serverTestFailed) {
71 return;
72 }
73
74 QList<int> capa = noEncCapa;
75 if (ui.encryptionSsl->isChecked()) {
76 capa = sslCapa;
77 } else if (ui.encryptionTls->isChecked()) {
78 capa = tlsCapa;
79 }
80
81 ui.authCombo->clear();
82 for (int authType : std::as_const(capa)) {
83 addAuthenticationItem(ui.authCombo, authType);
84 }
85
86 if (transport->isValid()) {
87 const int idx = ui.authCombo->findData(transport->authenticationType());
88
89 if (idx != -1) {
90 ui.authCombo->setCurrentIndex(idx);
91 }
92 }
93
94 if (capa.isEmpty()) {
95 ui.noAuthPossible->setVisible(true);
96 ui.kcfg_requiresAuthentication->setChecked(false);
97 ui.kcfg_requiresAuthentication->setEnabled(false);
98 ui.kcfg_requiresAuthentication->setVisible(false);
99 ui.authCombo->setEnabled(false);
100 ui.authLabel->setEnabled(false);
101 } else {
102 ui.noAuthPossible->setVisible(false);
103 ui.kcfg_requiresAuthentication->setEnabled(true);
104 ui.kcfg_requiresAuthentication->setVisible(true);
105 ui.authCombo->setEnabled(true);
106 ui.authLabel->setEnabled(true);
107 enablePasswordLine();
108 }
109 }
110};
111
112SMTPConfigWidget::SMTPConfigWidget(Transport *transport, QWidget *parent)
113 : TransportConfigWidget(*new SMTPConfigWidgetPrivate, transport, parent)
114{
115 init();
116}
117
118static void checkHighestEnabledButton(QButtonGroup *group)
119{
120 Q_ASSERT(group);
121
122 for (int i = group->buttons().count() - 1; i >= 0; --i) {
123 QAbstractButton *b = group->buttons().at(i);
124 if (b && b->isEnabled()) {
125 b->animateClick();
126 return;
127 }
128 }
129}
130
131void SMTPConfigWidget::init()
132{
134 d->serverTest = nullptr;
135
136 connect(TransportManager::self(), &TransportManager::passwordsChanged, this, &SMTPConfigWidget::passwordsLoaded);
137
138 d->serverTestFailed = false;
139
140 d->ui.setupUi(this);
141 d->ui.password->setRevealPasswordMode(KAuthorized::authorize(QStringLiteral("lineedit_reveal_password")) ? KPassword::RevealMode::OnlyNew
142 : KPassword::RevealMode::Never);
143 d->manager->addWidget(this); // otherwise it doesn't find out about these widgets
144 d->manager->updateWidgets();
145
146 d->ui.password->setWhatsThis(i18n("The password to send to the server for authorization."));
147
148 d->ui.kcfg_userName->setClearButtonEnabled(true);
149 d->encryptionGroup = new QButtonGroup(this);
150 d->encryptionGroup->addButton(d->ui.encryptionNone, Transport::EnumEncryption::None);
151 d->encryptionGroup->addButton(d->ui.encryptionSsl, Transport::EnumEncryption::SSL);
152 d->encryptionGroup->addButton(d->ui.encryptionTls, Transport::EnumEncryption::TLS);
153
154 d->ui.encryptionNone->setChecked(d->transport->encryption() == Transport::EnumEncryption::None);
155 d->ui.encryptionSsl->setChecked(d->transport->encryption() == Transport::EnumEncryption::SSL);
156 d->ui.encryptionTls->setChecked(d->transport->encryption() == Transport::EnumEncryption::TLS);
157
158 d->ui.checkCapabilitiesProgress->setFormat(i18nc("Percent value; %p is the value, % is the percent sign", "%p%"));
159
160 d->resetAuthCapabilities();
161
162 if (!KProtocolInfo::capabilities(SMTP_PROTOCOL).contains(QLatin1StringView("SASL"))) {
163 d->ui.authCombo->removeItem(d->ui.authCombo->findData(Transport::EnumAuthenticationType::NTLM));
164 d->ui.authCombo->removeItem(d->ui.authCombo->findData(Transport::EnumAuthenticationType::GSSAPI));
165 }
166
167 connect(d->ui.checkCapabilities, &QPushButton::clicked, this, &SMTPConfigWidget::checkSmtpCapabilities);
168 connect(d->ui.kcfg_host, &QLineEdit::textChanged, this, &SMTPConfigWidget::hostNameChanged);
169
170 connect(d->encryptionGroup, &QButtonGroup::buttonClicked, this, &SMTPConfigWidget::encryptionAbstractButtonChanged);
171 connect(d->ui.kcfg_requiresAuthentication, &QCheckBox::toggled, this, &SMTPConfigWidget::ensureValidAuthSelection);
172 connect(d->ui.kcfg_storePassword, &QCheckBox::toggled, this, &SMTPConfigWidget::enablePasswordLine);
173 if (!d->transport->isValid()) {
174 checkHighestEnabledButton(d->encryptionGroup);
175 }
176
177 // load the password
178 d->transport->updatePasswordState();
179 if (d->transport->isComplete()) {
180 d->ui.password->setPassword(d->transport->password());
181 } else {
182 if (d->transport->requiresAuthentication()) {
184 }
185 }
186
187 hostNameChanged(d->transport->host());
188}
189
190void SMTPConfigWidget::enablePasswordLine()
191{
193 d->enablePasswordLine();
194}
195
196void SMTPConfigWidget::checkSmtpCapabilities()
197{
199
200 d->serverTest = new ServerTest(this);
201 d->serverTest->setProtocol(SMTP_PROTOCOL);
202 d->serverTest->setServer(d->ui.kcfg_host->text().trimmed());
203 if (d->ui.kcfg_specifyHostname->isChecked()) {
204 d->serverTest->setFakeHostname(d->ui.kcfg_localHostname->text());
205 }
206 QAbstractButton *encryptionChecked = d->encryptionGroup->checkedButton();
207 if (encryptionChecked == d->ui.encryptionNone) {
208 d->serverTest->setPort(Transport::EnumEncryption::None, d->ui.kcfg_port->value());
209 } else if (encryptionChecked == d->ui.encryptionSsl) {
210 d->serverTest->setPort(Transport::EnumEncryption::SSL, d->ui.kcfg_port->value());
211 }
212 d->serverTest->setProgressBar(d->ui.checkCapabilitiesProgress);
213 d->ui.checkCapabilitiesStack->setCurrentIndex(1);
214 qApp->setOverrideCursor(Qt::BusyCursor);
215
216 connect(d->serverTest, &ServerTest::finished, this, &SMTPConfigWidget::slotFinished);
217 connect(d->serverTest, &ServerTest::finished, qApp, []() {
218 qApp->restoreOverrideCursor();
219 });
220 d->ui.checkCapabilities->setEnabled(false);
221 d->serverTest->start();
222 d->serverTestFailed = false;
223}
224
226{
228 Q_ASSERT(d->manager);
229 d->manager->updateSettings();
230 if (!d->ui.kcfg_storePassword->isChecked() && d->ui.kcfg_requiresAuthentication->isChecked()) {
231 // Delete stored password
232 TransportManager::self()->removePasswordFromWallet(d->transport->id());
233 }
234 d->transport->setPassword(d->ui.password->password());
235
236 KConfigGroup group(d->transport->config(), d->transport->currentGroup());
237 const int index = d->ui.authCombo->currentIndex();
238 if (index >= 0) {
239 group.writeEntry("authtype", d->ui.authCombo->itemData(index).toInt());
240 }
241
242 if (d->ui.encryptionNone->isChecked()) {
243 d->transport->setEncryption(Transport::EnumEncryption::None);
244 } else if (d->ui.encryptionSsl->isChecked()) {
245 d->transport->setEncryption(Transport::EnumEncryption::SSL);
246 } else if (d->ui.encryptionTls->isChecked()) {
247 d->transport->setEncryption(Transport::EnumEncryption::TLS);
248 }
249
251}
252
253void SMTPConfigWidget::passwordsLoaded()
254{
256
257 // Load the password from the original to our cloned copy
258 d->transport->updatePasswordState();
259
260 if (d->ui.password->password().isEmpty()) {
261 d->ui.password->setPassword(d->transport->password());
262 }
263}
264
265// TODO rename
266void SMTPConfigWidget::slotFinished(const QList<int> &results)
267{
269
270 d->ui.checkCapabilitiesStack->setCurrentIndex(0);
271
272 d->ui.checkCapabilities->setEnabled(true);
273 d->serverTest->deleteLater();
274
275 // If the servertest did not find any usable authentication modes, assume the
276 // connection failed and don't disable any of the radioboxes.
277 if (results.isEmpty()) {
278 KMessageBox::error(this, i18n("Failed to check capabilities. Please verify port and authentication mode."), i18n("Check Capabilities Failed"));
279 d->serverTestFailed = true;
280 d->serverTest->deleteLater();
281 return;
282 }
283
284 // encryption method
285 d->ui.encryptionNone->setEnabled(results.contains(Transport::EnumEncryption::None));
286 d->ui.encryptionSsl->setEnabled(results.contains(Transport::EnumEncryption::SSL));
287 d->ui.encryptionTls->setEnabled(results.contains(Transport::EnumEncryption::TLS));
288 checkHighestEnabledButton(d->encryptionGroup);
289
290 d->noEncCapa = d->serverTest->normalProtocols();
291 if (d->ui.encryptionTls->isEnabled()) {
292 d->tlsCapa = d->serverTest->tlsProtocols();
293 } else {
294 d->tlsCapa.clear();
295 }
296 d->sslCapa = d->serverTest->secureProtocols();
297 d->updateAuthCapbilities();
298 // Show correct port from capabilities.
299 if (d->ui.encryptionSsl->isEnabled()) {
300 const int portValue = d->serverTest->port(Transport::EnumEncryption::SSL);
301 d->ui.kcfg_port->setValue(portValue == -1 ? SMTPS_PORT : portValue);
302 } else if (d->ui.encryptionNone->isEnabled()) {
303 const int portValue = d->serverTest->port(Transport::EnumEncryption::None);
304 d->ui.kcfg_port->setValue(portValue == -1 ? SMTP_PORT : portValue);
305 }
306 d->serverTest->deleteLater();
307}
308
309void SMTPConfigWidget::hostNameChanged(const QString &text)
310{
311 // TODO: really? is this done at every change? wtf
312
314
315 // sanitize hostname...
316 const int pos = d->ui.kcfg_host->cursorPosition();
317 d->ui.kcfg_host->blockSignals(true);
318 d->ui.kcfg_host->setText(text.trimmed());
319 d->ui.kcfg_host->blockSignals(false);
320 d->ui.kcfg_host->setCursorPosition(pos);
321
322 d->resetAuthCapabilities();
323 if (d->encryptionGroup) {
324 for (int i = 0; i < d->encryptionGroup->buttons().count(); ++i) {
325 d->encryptionGroup->buttons().at(i)->setEnabled(true);
326 }
327 }
328}
329
330void SMTPConfigWidget::ensureValidAuthSelection()
331{
333
334 // adjust available authentication methods
335 d->updateAuthCapbilities();
336 d->enablePasswordLine();
337}
338
339void SMTPConfigWidget::encryptionAbstractButtonChanged(QAbstractButton *button)
340{
342 if (button) {
343 encryptionChanged(d->encryptionGroup->id(button));
344 }
345}
346
347void SMTPConfigWidget::encryptionChanged(int enc)
348{
350 qCDebug(MAILTRANSPORT_SMTP_LOG) << enc;
351
352 // adjust port
353 if (enc == Transport::EnumEncryption::SSL) {
354 if (d->ui.kcfg_port->value() == SMTP_PORT) {
355 d->ui.kcfg_port->setValue(SMTPS_PORT);
356 }
357 } else {
358 if (d->ui.kcfg_port->value() == SMTPS_PORT) {
359 d->ui.kcfg_port->setValue(SMTP_PORT);
360 }
361 }
362
363 ensureValidAuthSelection();
364}
365
366#include "moc_smtpconfigwidget.cpp"
static Q_INVOKABLE bool authorize(const QString &action)
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
This class can be used to test certain server to see if they support stuff.
Definition servertest.h:30
void finished(const QList< int > &)
This will be emitted when the test is done.
virtual void apply()
Saves the transport's settings.
void loadPasswordsAsync()
Tries to load passwords asynchronously from KWallet if needed.
void passwordsChanged()
Emitted when passwords have been loaded from the wallet.
static TransportManager * self()
Returns the TransportManager instance.
Represents the settings of a specific mail transport.
Definition transport.h:33
QString authenticationTypeString() const
Returns a string representation of the authentication type.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Internal file containing constant definitions etc.
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QCA_EXPORT void init()
void clicked(bool checked)
void toggled(bool checked)
void buttonClicked(QAbstractButton *button)
QList< QAbstractButton * > buttons() const const
void addItem(const QIcon &icon, const QString &text, const QVariant &userData)
void textChanged(const QString &text)
void clear()
bool contains(const AT &value) const const
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString trimmed() const const
BusyCursor
bool isEnabled() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:12:37 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.