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"