File indexing completed on 2024-09-22 04:47:56

0001 /*
0002     SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
0003 
0004     Based on MailTransport code by:
0005     SPDX-FileCopyrightText: 2006-2007 Volker Krause <vkrause@kde.org>
0006     SPDX-FileCopyrightText: 2007 KovoKs <kovoks@kovoks.nl>
0007 
0008     Based on KMail code by:
0009     SPDX-FileCopyrightText: 2001-2002 Michael Haeckel <haeckel@kde.org>
0010 
0011     SPDX-License-Identifier: LGPL-2.0-or-later
0012 */
0013 
0014 #include "smtpconfigwidget.h"
0015 #include "ui_smtpsettings.h"
0016 
0017 #include "mailtransport_defs.h"
0018 #include "mailtransportplugin_smtp_debug.h"
0019 #include "servertest.h"
0020 #include "transport.h"
0021 #include "transportmanager.h"
0022 #include "widgets/transportconfigwidget_p.h"
0023 
0024 #include <QAbstractButton>
0025 #include <QButtonGroup>
0026 
0027 #include "mailtransport_debug.h"
0028 #include <KAuthorized>
0029 #include <KMessageBox>
0030 #include <KProtocolInfo>
0031 
0032 using namespace MailTransport;
0033 
0034 class MailTransport::SMTPConfigWidgetPrivate : public TransportConfigWidgetPrivate
0035 {
0036 public:
0037     ::Ui::SMTPSettings ui;
0038 
0039     ServerTest *serverTest = nullptr;
0040     QButtonGroup *encryptionGroup = nullptr;
0041 
0042     // detected authentication capabilities
0043     QList<int> noEncCapa, sslCapa, tlsCapa;
0044 
0045     bool serverTestFailed;
0046 
0047     static void addAuthenticationItem(QComboBox *combo, int authenticationType)
0048     {
0049         combo->addItem(Transport::authenticationTypeString(authenticationType), QVariant(authenticationType));
0050     }
0051 
0052     void resetAuthCapabilities()
0053     {
0054         noEncCapa.clear();
0055         noEncCapa << Transport::EnumAuthenticationType::LOGIN << Transport::EnumAuthenticationType::PLAIN << Transport::EnumAuthenticationType::CRAM_MD5
0056                   << Transport::EnumAuthenticationType::DIGEST_MD5 << Transport::EnumAuthenticationType::NTLM << Transport::EnumAuthenticationType::GSSAPI
0057                   << Transport::EnumAuthenticationType::XOAUTH2;
0058         sslCapa = tlsCapa = noEncCapa;
0059         updateAuthCapbilities();
0060     }
0061 
0062     void enablePasswordLine()
0063     {
0064         ui.password->setEnabled(ui.kcfg_storePassword->isChecked());
0065     }
0066 
0067     void updateAuthCapbilities()
0068     {
0069         if (serverTestFailed) {
0070             return;
0071         }
0072 
0073         QList<int> capa = noEncCapa;
0074         if (ui.encryptionSsl->isChecked()) {
0075             capa = sslCapa;
0076         } else if (ui.encryptionTls->isChecked()) {
0077             capa = tlsCapa;
0078         }
0079 
0080         ui.authCombo->clear();
0081         for (int authType : std::as_const(capa)) {
0082             addAuthenticationItem(ui.authCombo, authType);
0083         }
0084 
0085         if (transport->isValid()) {
0086             const int idx = ui.authCombo->findData(transport->authenticationType());
0087 
0088             if (idx != -1) {
0089                 ui.authCombo->setCurrentIndex(idx);
0090             }
0091         }
0092 
0093         if (capa.isEmpty()) {
0094             ui.noAuthPossible->setVisible(true);
0095             ui.kcfg_requiresAuthentication->setChecked(false);
0096             ui.kcfg_requiresAuthentication->setEnabled(false);
0097             ui.kcfg_requiresAuthentication->setVisible(false);
0098             ui.authCombo->setEnabled(false);
0099             ui.authLabel->setEnabled(false);
0100         } else {
0101             ui.noAuthPossible->setVisible(false);
0102             ui.kcfg_requiresAuthentication->setEnabled(true);
0103             ui.kcfg_requiresAuthentication->setVisible(true);
0104             ui.authCombo->setEnabled(true);
0105             ui.authLabel->setEnabled(true);
0106             enablePasswordLine();
0107         }
0108     }
0109 };
0110 
0111 SMTPConfigWidget::SMTPConfigWidget(Transport *transport, QWidget *parent)
0112     : TransportConfigWidget(*new SMTPConfigWidgetPrivate, transport, parent)
0113 {
0114     init();
0115 }
0116 
0117 static void checkHighestEnabledButton(QButtonGroup *group)
0118 {
0119     Q_ASSERT(group);
0120 
0121     for (int i = group->buttons().count() - 1; i >= 0; --i) {
0122         QAbstractButton *b = group->buttons().at(i);
0123         if (b && b->isEnabled()) {
0124             b->animateClick();
0125             return;
0126         }
0127     }
0128 }
0129 
0130 void SMTPConfigWidget::init()
0131 {
0132     Q_D(SMTPConfigWidget);
0133     d->serverTest = nullptr;
0134 
0135     connect(TransportManager::self(), &TransportManager::passwordsChanged, this, &SMTPConfigWidget::passwordsLoaded);
0136 
0137     d->serverTestFailed = false;
0138 
0139     d->ui.setupUi(this);
0140     d->ui.password->setRevealPasswordAvailable(KAuthorized::authorize(QStringLiteral("lineedit_reveal_password")));
0141     d->manager->addWidget(this); // otherwise it doesn't find out about these widgets
0142     d->manager->updateWidgets();
0143 
0144     d->ui.password->setWhatsThis(i18n("The password to send to the server for authorization."));
0145 
0146     d->ui.kcfg_userName->setClearButtonEnabled(true);
0147     d->encryptionGroup = new QButtonGroup(this);
0148     d->encryptionGroup->addButton(d->ui.encryptionNone, Transport::EnumEncryption::None);
0149     d->encryptionGroup->addButton(d->ui.encryptionSsl, Transport::EnumEncryption::SSL);
0150     d->encryptionGroup->addButton(d->ui.encryptionTls, Transport::EnumEncryption::TLS);
0151 
0152     d->ui.encryptionNone->setChecked(d->transport->encryption() == Transport::EnumEncryption::None);
0153     d->ui.encryptionSsl->setChecked(d->transport->encryption() == Transport::EnumEncryption::SSL);
0154     d->ui.encryptionTls->setChecked(d->transport->encryption() == Transport::EnumEncryption::TLS);
0155 
0156     d->resetAuthCapabilities();
0157 
0158     if (!KProtocolInfo::capabilities(SMTP_PROTOCOL).contains(QLatin1StringView("SASL"))) {
0159         d->ui.authCombo->removeItem(d->ui.authCombo->findData(Transport::EnumAuthenticationType::NTLM));
0160         d->ui.authCombo->removeItem(d->ui.authCombo->findData(Transport::EnumAuthenticationType::GSSAPI));
0161     }
0162 
0163     connect(d->ui.checkCapabilities, &QPushButton::clicked, this, &SMTPConfigWidget::checkSmtpCapabilities);
0164     connect(d->ui.kcfg_host, &QLineEdit::textChanged, this, &SMTPConfigWidget::hostNameChanged);
0165 
0166     connect(d->encryptionGroup, &QButtonGroup::buttonClicked, this, &SMTPConfigWidget::encryptionAbstractButtonChanged);
0167     connect(d->ui.kcfg_requiresAuthentication, &QCheckBox::toggled, this, &SMTPConfigWidget::ensureValidAuthSelection);
0168     connect(d->ui.kcfg_storePassword, &QCheckBox::toggled, this, &SMTPConfigWidget::enablePasswordLine);
0169     if (!d->transport->isValid()) {
0170         checkHighestEnabledButton(d->encryptionGroup);
0171     }
0172 
0173     // load the password
0174     d->transport->updatePasswordState();
0175     if (d->transport->isComplete()) {
0176         d->ui.password->setPassword(d->transport->password());
0177     } else {
0178         if (d->transport->requiresAuthentication()) {
0179             TransportManager::self()->loadPasswordsAsync();
0180         }
0181     }
0182 
0183     hostNameChanged(d->transport->host());
0184 }
0185 
0186 void SMTPConfigWidget::enablePasswordLine()
0187 {
0188     Q_D(SMTPConfigWidget);
0189     d->enablePasswordLine();
0190 }
0191 
0192 void SMTPConfigWidget::checkSmtpCapabilities()
0193 {
0194     Q_D(SMTPConfigWidget);
0195 
0196     d->serverTest = new ServerTest(this);
0197     d->serverTest->setProtocol(SMTP_PROTOCOL);
0198     d->serverTest->setServer(d->ui.kcfg_host->text().trimmed());
0199     if (d->ui.kcfg_specifyHostname->isChecked()) {
0200         d->serverTest->setFakeHostname(d->ui.kcfg_localHostname->text());
0201     }
0202     QAbstractButton *encryptionChecked = d->encryptionGroup->checkedButton();
0203     if (encryptionChecked == d->ui.encryptionNone) {
0204         d->serverTest->setPort(Transport::EnumEncryption::None, d->ui.kcfg_port->value());
0205     } else if (encryptionChecked == d->ui.encryptionSsl) {
0206         d->serverTest->setPort(Transport::EnumEncryption::SSL, d->ui.kcfg_port->value());
0207     }
0208     d->serverTest->setProgressBar(d->ui.checkCapabilitiesProgress);
0209     d->ui.checkCapabilitiesStack->setCurrentIndex(1);
0210     qApp->setOverrideCursor(Qt::BusyCursor);
0211 
0212     connect(d->serverTest, &ServerTest::finished, this, &SMTPConfigWidget::slotFinished);
0213     connect(d->serverTest, &ServerTest::finished, qApp, []() {
0214         qApp->restoreOverrideCursor();
0215     });
0216     d->ui.checkCapabilities->setEnabled(false);
0217     d->serverTest->start();
0218     d->serverTestFailed = false;
0219 }
0220 
0221 void SMTPConfigWidget::apply()
0222 {
0223     Q_D(SMTPConfigWidget);
0224     Q_ASSERT(d->manager);
0225     d->manager->updateSettings();
0226     if (!d->ui.kcfg_storePassword->isChecked() && d->ui.kcfg_requiresAuthentication->isChecked()) {
0227         // Delete stored password
0228         TransportManager::self()->removePasswordFromWallet(d->transport->id());
0229     }
0230     d->transport->setPassword(d->ui.password->password());
0231 
0232     KConfigGroup group(d->transport->config(), d->transport->currentGroup());
0233     const int index = d->ui.authCombo->currentIndex();
0234     if (index >= 0) {
0235         group.writeEntry("authtype", d->ui.authCombo->itemData(index).toInt());
0236     }
0237 
0238     if (d->ui.encryptionNone->isChecked()) {
0239         d->transport->setEncryption(Transport::EnumEncryption::None);
0240     } else if (d->ui.encryptionSsl->isChecked()) {
0241         d->transport->setEncryption(Transport::EnumEncryption::SSL);
0242     } else if (d->ui.encryptionTls->isChecked()) {
0243         d->transport->setEncryption(Transport::EnumEncryption::TLS);
0244     }
0245 
0246     TransportConfigWidget::apply();
0247 }
0248 
0249 void SMTPConfigWidget::passwordsLoaded()
0250 {
0251     Q_D(SMTPConfigWidget);
0252 
0253     // Load the password from the original to our cloned copy
0254     d->transport->updatePasswordState();
0255 
0256     if (d->ui.password->password().isEmpty()) {
0257         d->ui.password->setPassword(d->transport->password());
0258     }
0259 }
0260 
0261 // TODO rename
0262 void SMTPConfigWidget::slotFinished(const QList<int> &results)
0263 {
0264     Q_D(SMTPConfigWidget);
0265 
0266     d->ui.checkCapabilitiesStack->setCurrentIndex(0);
0267 
0268     d->ui.checkCapabilities->setEnabled(true);
0269     d->serverTest->deleteLater();
0270 
0271     // If the servertest did not find any usable authentication modes, assume the
0272     // connection failed and don't disable any of the radioboxes.
0273     if (results.isEmpty()) {
0274         KMessageBox::error(this, i18n("Failed to check capabilities. Please verify port and authentication mode."), i18n("Check Capabilities Failed"));
0275         d->serverTestFailed = true;
0276         d->serverTest->deleteLater();
0277         return;
0278     }
0279 
0280     // encryption method
0281     d->ui.encryptionNone->setEnabled(results.contains(Transport::EnumEncryption::None));
0282     d->ui.encryptionSsl->setEnabled(results.contains(Transport::EnumEncryption::SSL));
0283     d->ui.encryptionTls->setEnabled(results.contains(Transport::EnumEncryption::TLS));
0284     checkHighestEnabledButton(d->encryptionGroup);
0285 
0286     d->noEncCapa = d->serverTest->normalProtocols();
0287     if (d->ui.encryptionTls->isEnabled()) {
0288         d->tlsCapa = d->serverTest->tlsProtocols();
0289     } else {
0290         d->tlsCapa.clear();
0291     }
0292     d->sslCapa = d->serverTest->secureProtocols();
0293     d->updateAuthCapbilities();
0294     // Show correct port from capabilities.
0295     if (d->ui.encryptionSsl->isEnabled()) {
0296         const int portValue = d->serverTest->port(Transport::EnumEncryption::SSL);
0297         d->ui.kcfg_port->setValue(portValue == -1 ? SMTPS_PORT : portValue);
0298     } else if (d->ui.encryptionNone->isEnabled()) {
0299         const int portValue = d->serverTest->port(Transport::EnumEncryption::None);
0300         d->ui.kcfg_port->setValue(portValue == -1 ? SMTP_PORT : portValue);
0301     }
0302     d->serverTest->deleteLater();
0303 }
0304 
0305 void SMTPConfigWidget::hostNameChanged(const QString &text)
0306 {
0307     // TODO: really? is this done at every change? wtf
0308 
0309     Q_D(SMTPConfigWidget);
0310 
0311     // sanitize hostname...
0312     const int pos = d->ui.kcfg_host->cursorPosition();
0313     d->ui.kcfg_host->blockSignals(true);
0314     d->ui.kcfg_host->setText(text.trimmed());
0315     d->ui.kcfg_host->blockSignals(false);
0316     d->ui.kcfg_host->setCursorPosition(pos);
0317 
0318     d->resetAuthCapabilities();
0319     if (d->encryptionGroup) {
0320         for (int i = 0; i < d->encryptionGroup->buttons().count(); ++i) {
0321             d->encryptionGroup->buttons().at(i)->setEnabled(true);
0322         }
0323     }
0324 }
0325 
0326 void SMTPConfigWidget::ensureValidAuthSelection()
0327 {
0328     Q_D(SMTPConfigWidget);
0329 
0330     // adjust available authentication methods
0331     d->updateAuthCapbilities();
0332     d->enablePasswordLine();
0333 }
0334 
0335 void SMTPConfigWidget::encryptionAbstractButtonChanged(QAbstractButton *button)
0336 {
0337     Q_D(SMTPConfigWidget);
0338     if (button) {
0339         encryptionChanged(d->encryptionGroup->id(button));
0340     }
0341 }
0342 
0343 void SMTPConfigWidget::encryptionChanged(int enc)
0344 {
0345     Q_D(SMTPConfigWidget);
0346     qCDebug(MAILTRANSPORT_SMTP_LOG) << enc;
0347 
0348     // adjust port
0349     if (enc == Transport::EnumEncryption::SSL) {
0350         if (d->ui.kcfg_port->value() == SMTP_PORT) {
0351             d->ui.kcfg_port->setValue(SMTPS_PORT);
0352         }
0353     } else {
0354         if (d->ui.kcfg_port->value() == SMTPS_PORT) {
0355             d->ui.kcfg_port->setValue(SMTP_PORT);
0356         }
0357     }
0358 
0359     ensureValidAuthSelection();
0360 }
0361 
0362 #include "moc_smtpconfigwidget.cpp"