smtpconfigwidget.cpp 11.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
    Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>

    Based on MailTransport code by:
    Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
    Copyright (c) 2007 KovoKs <kovoks@kovoks.nl>

    Based on KMail code by:
    Copyright (c) 2001-2002 Michael Haeckel <haeckel@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "smtpconfigwidget.h"
Volker Krause's avatar
Volker Krause committed
28
#include "ui_smtpsettings.h"
29

30
#include "widgets/transportconfigwidget_p.h"
31 32 33 34
#include "transport.h"
#include "transportmanager.h"
#include "servertest.h"
#include "mailtransport_defs.h"
35
#include "mailtransportplugin_smtp_debug.h"
36 37 38 39 40

#include <QAbstractButton>
#include <QButtonGroup>

#include <KProtocolInfo>
Laurent Montel's avatar
Laurent Montel committed
41
#include "mailtransport_debug.h"
42
#include <KMessageBox>
43 44 45 46 47

using namespace MailTransport;

class MailTransport::SMTPConfigWidgetPrivate : public TransportConfigWidgetPrivate
{
Laurent Montel's avatar
Laurent Montel committed
48
public:
Luboš Luňák's avatar
Luboš Luňák committed
49
    ::Ui::SMTPSettings ui;
50 51 52 53 54

    ServerTest *serverTest;
    QButtonGroup *encryptionGroup;

    // detected authentication capabilities
Laurent Montel's avatar
Laurent Montel committed
55
    QVector<int> noEncCapa, sslCapa, tlsCapa;
56 57 58

    bool serverTestFailed;

Laurent Montel's avatar
Laurent Montel committed
59
    static void addAuthenticationItem(QComboBox *combo, int authenticationType)
60
    {
Laurent Montel's avatar
Laurent Montel committed
61 62
        combo->addItem(Transport::authenticationTypeString(authenticationType),
                       QVariant(authenticationType));
63 64
    }

65 66
    void resetAuthCapabilities()
    {
Laurent Montel's avatar
Laurent Montel committed
67 68 69 70 71 72 73 74 75
        noEncCapa.clear();
        noEncCapa << Transport::EnumAuthenticationType::LOGIN
                  << Transport::EnumAuthenticationType::PLAIN
                  << Transport::EnumAuthenticationType::CRAM_MD5
                  << Transport::EnumAuthenticationType::DIGEST_MD5
                  << Transport::EnumAuthenticationType::NTLM
                  << Transport::EnumAuthenticationType::GSSAPI;
        sslCapa = tlsCapa = noEncCapa;
        updateAuthCapbilities();
76 77 78 79
    }

    void updateAuthCapbilities()
    {
Laurent Montel's avatar
Laurent Montel committed
80 81 82
        if (serverTestFailed) {
            return;
        }
83

Laurent Montel's avatar
Laurent Montel committed
84
        QVector<int> capa = noEncCapa;
85
        if (ui.encryptionSsl->isChecked()) {
Laurent Montel's avatar
Laurent Montel committed
86
            capa = sslCapa;
87
        } else if (ui.encryptionTls->isChecked()) {
Laurent Montel's avatar
Laurent Montel committed
88 89
            capa = tlsCapa;
        }
90

Laurent Montel's avatar
Laurent Montel committed
91
        ui.authCombo->clear();
Laurent Montel's avatar
Laurent Montel committed
92
        for (int authType : qAsConst(capa)) {
Laurent Montel's avatar
Laurent Montel committed
93 94
            addAuthenticationItem(ui.authCombo, authType);
        }
95

Laurent Montel's avatar
Laurent Montel committed
96 97
        if (transport->isValid()) {
            const int idx = ui.authCombo->findData(transport->authenticationType());
98

Laurent Montel's avatar
Laurent Montel committed
99 100 101 102 103
            if (idx != -1) {
                ui.authCombo->setCurrentIndex(idx);
            }
        }

Laurent Montel's avatar
Laurent Montel committed
104
        if (capa.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
105 106 107 108 109 110 111 112 113 114 115 116
            ui.noAuthPossible->setVisible(true);
            ui.kcfg_requiresAuthentication->setChecked(false);
            ui.kcfg_requiresAuthentication->setEnabled(false);
            ui.kcfg_requiresAuthentication->setVisible(false);
            ui.authCombo->setEnabled(false);
            ui.authLabel->setEnabled(false);
        } else {
            ui.noAuthPossible->setVisible(false);
            ui.kcfg_requiresAuthentication->setEnabled(true);
            ui.kcfg_requiresAuthentication->setVisible(true);
            ui.authCombo->setEnabled(true);
            ui.authLabel->setEnabled(true);
117
        }
118 119 120
    }
};

Laurent Montel's avatar
Laurent Montel committed
121 122
SMTPConfigWidget::SMTPConfigWidget(Transport *transport, QWidget *parent)
    : TransportConfigWidget(*new SMTPConfigWidgetPrivate, transport, parent)
123
{
Laurent Montel's avatar
Laurent Montel committed
124
    init();
125 126
}

Laurent Montel's avatar
Laurent Montel committed
127
static void checkHighestEnabledButton(QButtonGroup *group)
128
{
Laurent Montel's avatar
Laurent Montel committed
129
    Q_ASSERT(group);
130

Laurent Montel's avatar
Laurent Montel committed
131 132 133 134 135 136
    for (int i = group->buttons().count() - 1; i >= 0; --i) {
        QAbstractButton *b = group->buttons().at(i);
        if (b && b->isEnabled()) {
            b->animateClick();
            return;
        }
137 138 139
    }
}

140 141
void SMTPConfigWidget::init()
{
Laurent Montel's avatar
Laurent Montel committed
142
    Q_D(SMTPConfigWidget);
Laurent Montel's avatar
Laurent Montel committed
143
    d->serverTest = nullptr;
Laurent Montel's avatar
Laurent Montel committed
144

Laurent Montel's avatar
Laurent Montel committed
145
    connect(TransportManager::self(), &TransportManager::passwordsChanged, this, &SMTPConfigWidget::passwordsLoaded);
Laurent Montel's avatar
Laurent Montel committed
146 147 148 149 150 151 152 153

    d->serverTestFailed = false;

    d->ui.setupUi(this);
    d->manager->addWidget(this);   // otherwise it doesn't find out about these widgets
    d->manager->updateWidgets();

    d->encryptionGroup = new QButtonGroup(this);
154 155 156 157 158 159 160
    d->encryptionGroup->addButton(d->ui.encryptionNone, Transport::EnumEncryption::None);
    d->encryptionGroup->addButton(d->ui.encryptionSsl, Transport::EnumEncryption::SSL);
    d->encryptionGroup->addButton(d->ui.encryptionTls, Transport::EnumEncryption::TLS);

    d->ui.encryptionNone->setChecked(d->transport->encryption() == Transport::EnumEncryption::None);
    d->ui.encryptionSsl->setChecked(d->transport->encryption() == Transport::EnumEncryption::SSL);
    d->ui.encryptionTls->setChecked(d->transport->encryption() == Transport::EnumEncryption::TLS);
Laurent Montel's avatar
Laurent Montel committed
161 162 163

    d->resetAuthCapabilities();

Laurent Montel's avatar
Laurent Montel committed
164
    if (!KProtocolInfo::capabilities(SMTP_PROTOCOL).contains(QStringLiteral("SASL"))) {
Laurent Montel's avatar
Laurent Montel committed
165 166 167 168
        d->ui.authCombo->removeItem(d->ui.authCombo->findData(
                                        Transport::EnumAuthenticationType::NTLM));
        d->ui.authCombo->removeItem(d->ui.authCombo->findData(
                                        Transport::EnumAuthenticationType::GSSAPI));
169 170
    }

Laurent Montel's avatar
Laurent Montel committed
171 172 173 174
    connect(d->ui.checkCapabilities, &QPushButton::clicked, this, &SMTPConfigWidget::checkSmtpCapabilities);
    connect(d->ui.kcfg_host, &QLineEdit::textChanged, this, &SMTPConfigWidget::hostNameChanged);
    connect(d->encryptionGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SMTPConfigWidget::encryptionChanged);
    connect(d->ui.kcfg_requiresAuthentication, &QCheckBox::toggled, this, &SMTPConfigWidget::ensureValidAuthSelection);
Laurent Montel's avatar
Laurent Montel committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190

    if (!d->transport->isValid()) {
        checkHighestEnabledButton(d->encryptionGroup);
    }

    // load the password
    d->transport->updatePasswordState();
    if (d->transport->isComplete()) {
        d->ui.password->setText(d->transport->password());
    } else {
        if (d->transport->requiresAuthentication()) {
            TransportManager::self()->loadPasswordsAsync();
        }
    }

    hostNameChanged(d->transport->host());
191 192 193 194
}

void SMTPConfigWidget::checkSmtpCapabilities()
{
Laurent Montel's avatar
Laurent Montel committed
195 196 197 198 199 200 201 202 203
    Q_D(SMTPConfigWidget);

    d->serverTest = new ServerTest(this);
    d->serverTest->setProtocol(SMTP_PROTOCOL);
    d->serverTest->setServer(d->ui.kcfg_host->text().trimmed());
    if (d->ui.kcfg_specifyHostname->isChecked()) {
        d->serverTest->setFakeHostname(d->ui.kcfg_localHostname->text());
    }
    QAbstractButton *encryptionChecked = d->encryptionGroup->checkedButton();
204
    if (encryptionChecked == d->ui.encryptionNone) {
Laurent Montel's avatar
Laurent Montel committed
205
        d->serverTest->setPort(Transport::EnumEncryption::None, d->ui.kcfg_port->value());
206
    } else if (encryptionChecked == d->ui.encryptionSsl) {
Laurent Montel's avatar
Laurent Montel committed
207 208 209 210
        d->serverTest->setPort(Transport::EnumEncryption::SSL, d->ui.kcfg_port->value());
    }
    d->serverTest->setProgressBar(d->ui.checkCapabilitiesProgress);
    d->ui.checkCapabilitiesStack->setCurrentIndex(1);
211
    qApp->setOverrideCursor(Qt::BusyCursor);
Laurent Montel's avatar
Laurent Montel committed
212

Laurent Montel's avatar
Laurent Montel committed
213
    connect(d->serverTest, &ServerTest::finished, this, &SMTPConfigWidget::slotFinished);
Laurent Montel's avatar
Laurent Montel committed
214 215 216
    connect(d->serverTest, &ServerTest::finished, qApp, [](){
        qApp->restoreOverrideCursor();
    });
Laurent Montel's avatar
Laurent Montel committed
217 218 219
    d->ui.checkCapabilities->setEnabled(false);
    d->serverTest->start();
    d->serverTestFailed = false;
220 221 222 223
}

void SMTPConfigWidget::apply()
{
Laurent Montel's avatar
Laurent Montel committed
224 225 226 227 228 229 230 231 232 233 234
    Q_D(SMTPConfigWidget);
    Q_ASSERT(d->manager);
    d->manager->updateSettings();
    d->transport->setPassword(d->ui.password->text());

    KConfigGroup group(d->transport->config(), d->transport->currentGroup());
    const int index = d->ui.authCombo->currentIndex();
    if (index >= 0) {
        group.writeEntry("authtype", d->ui.authCombo->itemData(index).toInt());
    }

Laurent Montel's avatar
Laurent Montel committed
235
    if (d->ui.encryptionNone->isChecked()) {
236
        d->transport->setEncryption(Transport::EnumEncryption::None);
237
    } else if (d->ui.encryptionSsl->isChecked()) {
238
        d->transport->setEncryption(Transport::EnumEncryption::SSL);
239
    } else if (d->ui.encryptionTls->isChecked()) {
240
        d->transport->setEncryption(Transport::EnumEncryption::TLS);
Laurent Montel's avatar
Laurent Montel committed
241
    }
242

Laurent Montel's avatar
Laurent Montel committed
243
    TransportConfigWidget::apply();
244 245 246 247
}

void SMTPConfigWidget::passwordsLoaded()
{
Laurent Montel's avatar
Laurent Montel committed
248
    Q_D(SMTPConfigWidget);
249

Laurent Montel's avatar
Laurent Montel committed
250 251
    // Load the password from the original to our cloned copy
    d->transport->updatePasswordState();
252

Laurent Montel's avatar
Laurent Montel committed
253 254 255
    if (d->ui.password->text().isEmpty()) {
        d->ui.password->setText(d->transport->password());
    }
256 257 258
}

// TODO rename
Laurent Montel's avatar
Laurent Montel committed
259
void SMTPConfigWidget::slotFinished(const QVector<int> &results)
260
{
Laurent Montel's avatar
Laurent Montel committed
261
    Q_D(SMTPConfigWidget);
262

Laurent Montel's avatar
Laurent Montel committed
263
    d->ui.checkCapabilitiesStack->setCurrentIndex(0);
264

Laurent Montel's avatar
Laurent Montel committed
265 266
    d->ui.checkCapabilities->setEnabled(true);
    d->serverTest->deleteLater();
267

Laurent Montel's avatar
Laurent Montel committed
268 269 270 271 272 273 274 275 276 277
    // If the servertest did not find any useable authentication modes, assume the
    // connection failed and don't disable any of the radioboxes.
    if (results.isEmpty()) {
        KMessageBox::error(this, i18n("Failed to check capabilities. Please verify port and authentication mode."), i18n("Check Capabilities Failed"));
        d->serverTestFailed = true;
        d->serverTest->deleteLater();
        return;
    }

    // encryption method
278 279 280
    d->ui.encryptionNone->setEnabled(results.contains(Transport::EnumEncryption::None));
    d->ui.encryptionSsl->setEnabled(results.contains(Transport::EnumEncryption::SSL));
    d->ui.encryptionTls->setEnabled(results.contains(Transport::EnumEncryption::TLS));
Laurent Montel's avatar
Laurent Montel committed
281 282 283
    checkHighestEnabledButton(d->encryptionGroup);

    d->noEncCapa = d->serverTest->normalProtocols();
284
    if (d->ui.encryptionTls->isEnabled()) {
Laurent Montel's avatar
Laurent Montel committed
285 286 287 288 289 290 291
        d->tlsCapa = d->serverTest->tlsProtocols();
    } else {
        d->tlsCapa.clear();
    }
    d->sslCapa = d->serverTest->secureProtocols();
    d->updateAuthCapbilities();
    //Show correct port from capabilities.
292
    if (d->ui.encryptionSsl->isEnabled()) {
Laurent Montel's avatar
Laurent Montel committed
293 294
        const int portValue = d->serverTest->port(Transport::EnumEncryption::SSL);
        d->ui.kcfg_port->setValue(portValue == -1 ? SMTPS_PORT : portValue);
295
    } else if (d->ui.encryptionNone->isEnabled()) {
Laurent Montel's avatar
Laurent Montel committed
296 297 298
        const int portValue = d->serverTest->port(Transport::EnumEncryption::None);
        d->ui.kcfg_port->setValue(portValue == -1 ? SMTP_PORT : portValue);
    }
299
    d->serverTest->deleteLater();
300 301
}

Laurent Montel's avatar
Laurent Montel committed
302
void SMTPConfigWidget::hostNameChanged(const QString &text)
303
{
Laurent Montel's avatar
Laurent Montel committed
304
    // TODO: really? is this done at every change? wtf
305

Laurent Montel's avatar
Laurent Montel committed
306
    Q_D(SMTPConfigWidget);
307

Laurent Montel's avatar
Laurent Montel committed
308
    // sanitize hostname...
Laurent Montel's avatar
Laurent Montel committed
309
    const int pos = d->ui.kcfg_host->cursorPosition();
Laurent Montel's avatar
Laurent Montel committed
310 311 312 313
    d->ui.kcfg_host->blockSignals(true);
    d->ui.kcfg_host->setText(text.trimmed());
    d->ui.kcfg_host->blockSignals(false);
    d->ui.kcfg_host->setCursorPosition(pos);
314

Laurent Montel's avatar
Laurent Montel committed
315
    d->resetAuthCapabilities();
Laurent Montel's avatar
Laurent Montel committed
316
    for (int i = 0; d->encryptionGroup && i < d->encryptionGroup->buttons().count(); ++i) {
Laurent Montel's avatar
Laurent Montel committed
317 318
        d->encryptionGroup->buttons().at(i)->setEnabled(true);
    }
319 320 321 322
}

void SMTPConfigWidget::ensureValidAuthSelection()
{
Laurent Montel's avatar
Laurent Montel committed
323
    Q_D(SMTPConfigWidget);
324

Laurent Montel's avatar
Laurent Montel committed
325 326
    // adjust available authentication methods
    d->updateAuthCapbilities();
327 328
}

Laurent Montel's avatar
Laurent Montel committed
329
void SMTPConfigWidget::encryptionChanged(int enc)
330
{
Laurent Montel's avatar
Laurent Montel committed
331
    Q_D(SMTPConfigWidget);
332
    qCDebug(MAILTRANSPORT_SMTP_LOG) << enc;
333

Laurent Montel's avatar
Laurent Montel committed
334 335 336 337 338 339 340 341 342
    // adjust port
    if (enc == Transport::EnumEncryption::SSL) {
        if (d->ui.kcfg_port->value() == SMTP_PORT) {
            d->ui.kcfg_port->setValue(SMTPS_PORT);
        }
    } else {
        if (d->ui.kcfg_port->value() == SMTPS_PORT) {
            d->ui.kcfg_port->setValue(SMTP_PORT);
        }
343 344
    }

Laurent Montel's avatar
Laurent Montel committed
345
    ensureValidAuthSelection();
346
}