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"
15using namespace Qt::Literals::StringLiterals;
16
17#include "ui_smtpsettings.h"
18
19#include "mailtransport_defs.h"
20#include "mailtransportplugin_smtp_debug.h"
21#include "servertest.h"
22#include "transport.h"
23#include "transportmanager.h"
24#include "widgets/transportactivitiesabstractplugin.h"
25#include "widgets/transportconfigwidget_p.h"
26
27#include <QAbstractButton>
28#include <QButtonGroup>
29
30#include <KAuthorized>
31#include <KLocalizedString>
32#include <KMessageBox>
33#include <KPluginFactory>
34#include <KPluginMetaData>
35#include <KProtocolInfo>
36
37using namespace MailTransport;
38
39class MailTransport::SMTPConfigWidgetPrivate : public TransportConfigWidgetPrivate
40{
41public:
42 ::Ui::SMTPSettings ui;
43
44 ServerTest *serverTest = nullptr;
45 QButtonGroup *encryptionGroup = nullptr;
46
47 // detected authentication capabilities
48 QList<int> noEncCapa, sslCapa, tlsCapa;
49
50 bool serverTestFailed;
51
52 static void addAuthenticationItem(QComboBox *combo, int authenticationType)
53 {
54 combo->addItem(Transport::authenticationTypeString(authenticationType), QVariant(authenticationType));
55 }
56
57 void resetAuthCapabilities()
58 {
59 noEncCapa.clear();
60 noEncCapa << Transport::EnumAuthenticationType::LOGIN << Transport::EnumAuthenticationType::PLAIN << Transport::EnumAuthenticationType::CRAM_MD5
61 << Transport::EnumAuthenticationType::DIGEST_MD5 << Transport::EnumAuthenticationType::NTLM << Transport::EnumAuthenticationType::GSSAPI
62 << Transport::EnumAuthenticationType::XOAUTH2;
63 sslCapa = tlsCapa = noEncCapa;
64 updateAuthCapbilities();
65 }
66
67 void enablePasswordLine()
68 {
69 ui.password->setEnabled(ui.kcfg_storePassword->isChecked());
70 }
71
72 void updateAuthCapbilities()
73 {
74 if (serverTestFailed) {
75 return;
76 }
77
78 QList<int> capa = noEncCapa;
79 if (ui.encryptionSsl->isChecked()) {
80 capa = sslCapa;
81 } else if (ui.encryptionTls->isChecked()) {
82 capa = tlsCapa;
83 }
84
85 ui.authCombo->clear();
86 for (int authType : std::as_const(capa)) {
87 addAuthenticationItem(ui.authCombo, authType);
88 }
89
90 if (transport->isValid()) {
91 const int idx = ui.authCombo->findData(transport->authenticationType());
92
93 if (idx != -1) {
94 ui.authCombo->setCurrentIndex(idx);
95 }
96 }
97
98 if (capa.isEmpty()) {
99 ui.noAuthPossible->setVisible(true);
100 ui.kcfg_requiresAuthentication->setChecked(false);
101 ui.kcfg_requiresAuthentication->setEnabled(false);
102 ui.kcfg_requiresAuthentication->setVisible(false);
103 ui.authCombo->setEnabled(false);
104 ui.authLabel->setEnabled(false);
105 } else {
106 ui.noAuthPossible->setVisible(false);
107 ui.kcfg_requiresAuthentication->setEnabled(true);
108 ui.kcfg_requiresAuthentication->setVisible(true);
109 ui.authCombo->setEnabled(true);
110 ui.authLabel->setEnabled(true);
111 enablePasswordLine();
112 }
113 }
114};
115
116SMTPConfigWidget::SMTPConfigWidget(Transport *transport, QWidget *parent)
117 : TransportConfigWidget(*new SMTPConfigWidgetPrivate, transport, parent)
118{
119 init();
120}
121
122static void checkHighestEnabledButton(QButtonGroup *group)
123{
124 Q_ASSERT(group);
125
126 for (int i = group->buttons().count() - 1; i >= 0; --i) {
127 QAbstractButton *b = group->buttons().at(i);
128 if (b && b->isEnabled()) {
129 b->animateClick();
130 return;
131 }
132 }
133}
134
135void SMTPConfigWidget::init()
136{
138 d->serverTest = nullptr;
139
140 connect(TransportManager::self(), &TransportManager::passwordsChanged, this, &SMTPConfigWidget::passwordsLoaded);
141
142 d->serverTestFailed = false;
143
144 d->ui.setupUi(this);
145 d->ui.tabWidget->tabBar()->setExpanding(true);
146 d->ui.password->setRevealPasswordMode(KAuthorized::authorize(QStringLiteral("lineedit_reveal_password")) ? KPassword::RevealMode::OnlyNew
147 : KPassword::RevealMode::Never);
148 d->manager->addWidget(this); // otherwise it doesn't find out about these widgets
149 d->manager->updateWidgets();
150
151 d->ui.password->setWhatsThis(i18n("The password to send to the server for authorization."));
152
153 d->ui.kcfg_userName->setClearButtonEnabled(true);
154 d->encryptionGroup = new QButtonGroup(this);
155 d->encryptionGroup->addButton(d->ui.encryptionNone, Transport::EnumEncryption::None);
156 d->encryptionGroup->addButton(d->ui.encryptionSsl, Transport::EnumEncryption::SSL);
157 d->encryptionGroup->addButton(d->ui.encryptionTls, Transport::EnumEncryption::TLS);
158
159 d->ui.encryptionNone->setChecked(d->transport->encryption() == Transport::EnumEncryption::None);
160 d->ui.encryptionSsl->setChecked(d->transport->encryption() == Transport::EnumEncryption::SSL);
161 d->ui.encryptionTls->setChecked(d->transport->encryption() == Transport::EnumEncryption::TLS);
162
163 d->ui.checkCapabilitiesProgress->setFormat(i18nc("Percent value; %p is the value, % is the percent sign", "%p%"));
164
165 d->resetAuthCapabilities();
166
167 if (!KProtocolInfo::capabilities(SMTP_PROTOCOL).contains("SASL"_L1)) {
168 d->ui.authCombo->removeItem(d->ui.authCombo->findData(Transport::EnumAuthenticationType::NTLM));
169 d->ui.authCombo->removeItem(d->ui.authCombo->findData(Transport::EnumAuthenticationType::GSSAPI));
170 }
171
172 connect(d->ui.checkCapabilities, &QPushButton::clicked, this, &SMTPConfigWidget::checkSmtpCapabilities);
173 connect(d->ui.kcfg_host, &QLineEdit::textChanged, this, &SMTPConfigWidget::hostNameChanged);
174
175 connect(d->encryptionGroup, &QButtonGroup::buttonClicked, this, &SMTPConfigWidget::encryptionAbstractButtonChanged);
176 connect(d->ui.kcfg_requiresAuthentication, &QCheckBox::toggled, this, &SMTPConfigWidget::ensureValidAuthSelection);
177 connect(d->ui.kcfg_storePassword, &QCheckBox::toggled, this, &SMTPConfigWidget::enablePasswordLine);
178 if (!d->transport->isValid()) {
179 checkHighestEnabledButton(d->encryptionGroup);
180 }
181
182 // load the password
183 d->transport->updatePasswordState();
184 if (d->transport->isComplete()) {
185 d->ui.password->setPassword(d->transport->password());
186 } else {
187 if (d->transport->requiresAuthentication()) {
189 }
190 }
191
192 hostNameChanged(d->transport->host());
193
194 const KPluginMetaData editWidgetPlugin(QStringLiteral("pim6/mailtransportactivities/kmailtransportactivitiesplugin"));
195
196 const auto result = KPluginFactory::instantiatePlugin<MailTransport::TransportActivitiesAbstractPlugin>(editWidgetPlugin);
197 if (result) {
198 mTransportActivitiesPlugin = result.plugin;
199 }
200 if (mTransportActivitiesPlugin) {
201 d->ui.tabWidget->addTab(mTransportActivitiesPlugin, i18n("Activities"));
202 TransportActivitiesAbstractPlugin::ActivitySettings settings{
203 d->transport->activities(),
204 d->transport->activitiesEnabled(),
205 };
206 mTransportActivitiesPlugin->setActivitiesSettings(settings);
207 }
208}
209
210void SMTPConfigWidget::enablePasswordLine()
211{
213 d->enablePasswordLine();
214}
215
216void SMTPConfigWidget::checkSmtpCapabilities()
217{
219
220 d->serverTest = new ServerTest(this);
221 d->serverTest->setProtocol(SMTP_PROTOCOL);
222 d->serverTest->setServer(d->ui.kcfg_host->text().trimmed());
223 if (d->ui.kcfg_specifyHostname->isChecked()) {
224 d->serverTest->setFakeHostname(d->ui.kcfg_localHostname->text());
225 }
226 QAbstractButton *encryptionChecked = d->encryptionGroup->checkedButton();
227 if (encryptionChecked == d->ui.encryptionNone) {
228 d->serverTest->setPort(Transport::EnumEncryption::None, d->ui.kcfg_port->value());
229 } else if (encryptionChecked == d->ui.encryptionSsl) {
230 d->serverTest->setPort(Transport::EnumEncryption::SSL, d->ui.kcfg_port->value());
231 }
232 d->serverTest->setProgressBar(d->ui.checkCapabilitiesProgress);
233 d->ui.checkCapabilitiesStack->setCurrentIndex(1);
234 qApp->setOverrideCursor(Qt::BusyCursor);
235
236 connect(d->serverTest, &ServerTest::finished, this, &SMTPConfigWidget::slotTestFinished);
237 connect(d->serverTest, &ServerTest::finished, qApp, []() {
238 qApp->restoreOverrideCursor();
239 });
240 d->ui.checkCapabilities->setEnabled(false);
241 d->serverTest->start();
242 d->serverTestFailed = false;
243}
244
246{
248 Q_ASSERT(d->manager);
249 d->manager->updateSettings();
250 if (!d->ui.kcfg_storePassword->isChecked() && d->ui.kcfg_requiresAuthentication->isChecked()) {
251 // Delete stored password
252 TransportManager::self()->removePasswordFromWallet(d->transport->id());
253 }
254 d->transport->setPassword(d->ui.password->password());
255
256 KConfigGroup group(d->transport->config(), d->transport->currentGroup());
257 const int index = d->ui.authCombo->currentIndex();
258 if (index >= 0) {
259 group.writeEntry("authtype", d->ui.authCombo->itemData(index).toInt());
260 }
261
262 if (d->ui.encryptionNone->isChecked()) {
263 d->transport->setEncryption(Transport::EnumEncryption::None);
264 } else if (d->ui.encryptionSsl->isChecked()) {
265 d->transport->setEncryption(Transport::EnumEncryption::SSL);
266 } else if (d->ui.encryptionTls->isChecked()) {
267 d->transport->setEncryption(Transport::EnumEncryption::TLS);
268 }
269
270 if (mTransportActivitiesPlugin) {
271 const TransportActivitiesAbstractPlugin::ActivitySettings settings = mTransportActivitiesPlugin->activitiesSettings();
272 d->transport->setActivities(settings.activities);
273 d->transport->setActivitiesEnabled(settings.enabled);
274 }
275
277}
278
279void SMTPConfigWidget::passwordsLoaded()
280{
282
283 // Load the password from the original to our cloned copy
284 d->transport->updatePasswordState();
285
286 if (d->ui.password->password().isEmpty()) {
287 d->ui.password->setPassword(d->transport->password());
288 }
289}
290
291void SMTPConfigWidget::slotTestFinished(const QList<int> &results)
292{
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 usable authentication modes, assume the
301 // connection failed and don't disable any of the radioboxes.
302 if (results.isEmpty()) {
304 i18n("Failed to check capabilities. Please verify port and authentication mode."),
305 i18nc("@title:window", "Check Capabilities Failed"));
306 d->serverTestFailed = true;
307 d->serverTest->deleteLater();
308 return;
309 }
310
311 // encryption method
312 d->ui.encryptionNone->setEnabled(results.contains(Transport::EnumEncryption::None));
313 d->ui.encryptionSsl->setEnabled(results.contains(Transport::EnumEncryption::SSL));
314 d->ui.encryptionTls->setEnabled(results.contains(Transport::EnumEncryption::TLS));
315 checkHighestEnabledButton(d->encryptionGroup);
316
317 d->noEncCapa = d->serverTest->normalProtocols();
318 if (d->ui.encryptionTls->isEnabled()) {
319 d->tlsCapa = d->serverTest->tlsProtocols();
320 } else {
321 d->tlsCapa.clear();
322 }
323 d->sslCapa = d->serverTest->secureProtocols();
324 d->updateAuthCapbilities();
325 // Show correct port from capabilities.
326 if (d->ui.encryptionSsl->isEnabled()) {
327 const int portValue = d->serverTest->port(Transport::EnumEncryption::SSL);
328 d->ui.kcfg_port->setValue(portValue == -1 ? SMTPS_PORT : portValue);
329 } else if (d->ui.encryptionNone->isEnabled()) {
330 const int portValue = d->serverTest->port(Transport::EnumEncryption::None);
331 d->ui.kcfg_port->setValue(portValue == -1 ? SMTP_PORT : portValue);
332 }
333 d->serverTest->deleteLater();
334}
335
336void SMTPConfigWidget::hostNameChanged(const QString &text)
337{
338 // TODO: really? is this done at every change? wtf
339
341
342 // sanitize hostname...
343 const int pos = d->ui.kcfg_host->cursorPosition();
344 d->ui.kcfg_host->blockSignals(true);
345 d->ui.kcfg_host->setText(text.trimmed());
346 d->ui.kcfg_host->blockSignals(false);
347 d->ui.kcfg_host->setCursorPosition(pos);
348
349 d->resetAuthCapabilities();
350 if (d->encryptionGroup) {
351 for (int i = 0; i < d->encryptionGroup->buttons().count(); ++i) {
352 d->encryptionGroup->buttons().at(i)->setEnabled(true);
353 }
354 }
355}
356
357void SMTPConfigWidget::ensureValidAuthSelection()
358{
360
361 // adjust available authentication methods
362 d->updateAuthCapbilities();
363 d->enablePasswordLine();
364}
365
366void SMTPConfigWidget::encryptionAbstractButtonChanged(QAbstractButton *button)
367{
369 if (button) {
370 encryptionChanged(d->encryptionGroup->id(button));
371 }
372}
373
374void SMTPConfigWidget::encryptionChanged(int enc)
375{
377 qCDebug(MAILTRANSPORT_SMTP_LOG) << enc;
378
379 // adjust port
380 if (enc == Transport::EnumEncryption::SSL) {
381 if (d->ui.kcfg_port->value() == SMTP_PORT) {
382 d->ui.kcfg_port->setValue(SMTPS_PORT);
383 }
384 } else {
385 if (d->ui.kcfg_port->value() == SMTPS_PORT) {
386 d->ui.kcfg_port->setValue(SMTP_PORT);
387 }
388 }
389
390 ensureValidAuthSelection();
391}
392
393#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 Sat Dec 21 2024 16:56:10 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.