diff --git a/resources/pop3/accountwidget.cpp b/resources/pop3/accountwidget.cpp index 244d35621..243aec38f 100644 --- a/resources/pop3/accountwidget.cpp +++ b/resources/pop3/accountwidget.cpp @@ -1,641 +1,642 @@ /* * Copyright (C) 2000 Espen Sand, espen@kde.org * Copyright 2009 Thomas McGuire * Copyright (c) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company * Copyright (C) 2010 Casey Link * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ // Local includes #include "accountwidget.h" #include "settings.h" #include "settingsadaptor.h" // KDEPIMLIBS includes #include #include #include #include #include #include // KDELIBS includes #include #include #include #include #include "pop3resource_debug.h" #include #include #include using namespace MailTransport; using namespace Akonadi; using namespace KWallet; namespace { class BusyCursorHelper : public QObject { public: inline BusyCursorHelper(QObject *parent) : QObject(parent) { #ifndef QT_NO_CURSOR qApp->setOverrideCursor(Qt::BusyCursor); #endif } inline ~BusyCursorHelper() { #ifndef QT_NO_CURSOR qApp->restoreOverrideCursor(); #endif } }; } -AccountWidget::AccountWidget(const QString &identifier, QWidget *parent) +AccountWidget::AccountWidget(Settings &settings, const QString &identifier, QWidget *parent) : QWidget(parent) , mValidator(this) , mIdentifier(identifier) + , mSettings(settings) { mValidator.setRegExp(QRegExp(QLatin1String("[A-Za-z0-9-_:.]*"))); setupWidgets(); } AccountWidget::~AccountWidget() { delete mWallet; mWallet = nullptr; delete mServerTest; mServerTest = nullptr; } void AccountWidget::setupWidgets() { QVBoxLayout *mainLayout = new QVBoxLayout(this); QWidget *page = new QWidget(this); mainLayout->addWidget(page); setupUi(page); // only letters, digits, '-', '.', ':' (IPv6) and '_' (for Windows // compatibility) are allowed hostEdit->setValidator(&mValidator); intervalSpin->setSuffix(ki18np(" minute", " minutes")); intervalSpin->setRange(ResourceSettings::self()->minimumCheckInterval(), 10000); intervalSpin->setSingleStep(1); connect(leaveOnServerCheck, &QCheckBox::clicked, this, &AccountWidget::slotLeaveOnServerClicked); connect(leaveOnServerDaysCheck, &QCheckBox::toggled, this, &AccountWidget::slotEnableLeaveOnServerDays); connect(leaveOnServerDaysSpin, QOverload::of(&QSpinBox::valueChanged), this, &AccountWidget::slotLeaveOnServerDaysChanged); connect(leaveOnServerCountCheck, &QCheckBox::toggled, this, &AccountWidget::slotEnableLeaveOnServerCount); connect(leaveOnServerCountSpin, QOverload::of(&QSpinBox::valueChanged), this, &AccountWidget::slotLeaveOnServerCountChanged); connect(leaveOnServerSizeCheck, &QCheckBox::toggled, this, &AccountWidget::slotEnableLeaveOnServerSize); connect(filterOnServerSizeSpin, QOverload::of(&QSpinBox::valueChanged), this, &AccountWidget::slotFilterOnServerSizeChanged); connect(filterOnServerCheck, &QCheckBox::toggled, filterOnServerSizeSpin, &QSpinBox::setEnabled); connect(filterOnServerCheck, &QCheckBox::clicked, this, &AccountWidget::slotFilterOnServerClicked); connect(checkCapabilities, &QPushButton::clicked, this, &AccountWidget::slotCheckPopCapabilities); encryptionButtonGroup = new QButtonGroup(page); encryptionButtonGroup->addButton(encryptionNone, Transport::EnumEncryption::None); encryptionButtonGroup->addButton(encryptionSSL, Transport::EnumEncryption::SSL); encryptionButtonGroup->addButton(encryptionTLS, Transport::EnumEncryption::TLS); connect(encryptionButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &AccountWidget::slotPopEncryptionChanged); connect(intervalCheck, &QCheckBox::toggled, this, &AccountWidget::slotEnablePopInterval); populateDefaultAuthenticationOptions(); folderRequester->setMimeTypeFilter( QStringList() << QStringLiteral("message/rfc822")); folderRequester->setAccessRightsFilter(Akonadi::Collection::CanCreateItem); folderRequester->changeCollectionDialogOptions(Akonadi::CollectionDialog::AllowToCreateNewChildCollection); connect(usePipeliningCheck, &QCheckBox::clicked, this, &AccountWidget::slotPipeliningClicked); // FIXME: Hide widgets which are not supported yet filterOnServerCheck->hide(); filterOnServerSizeSpin->hide(); } void AccountWidget::loadSettings() { - if (Settings::self()->name().isEmpty()) { + if (mSettings.name().isEmpty()) { nameEdit->setText(i18n("POP3 Account")); } else { - nameEdit->setText(Settings::self()->name()); + nameEdit->setText(mSettings.name()); } nameEdit->setFocus(); - loginEdit->setText(!Settings::self()->login().isEmpty() ? Settings::self()->login() + loginEdit->setText(!mSettings.login().isEmpty() ? mSettings.login() : KUser().loginName()); hostEdit->setText( - !Settings::self()->host().isEmpty() ? Settings::self()->host() + !mSettings.host().isEmpty() ? mSettings.host() : KEMailSettings().getSetting(KEMailSettings::InServer)); - hostEdit->setText(Settings::self()->host()); - portEdit->setValue(Settings::self()->port()); - precommand->setText(Settings::self()->precommand()); - usePipeliningCheck->setChecked(Settings::self()->pipelining()); - leaveOnServerCheck->setChecked(Settings::self()->leaveOnServer()); - leaveOnServerDaysCheck->setEnabled(Settings::self()->leaveOnServer()); - leaveOnServerDaysCheck->setChecked(Settings::self()->leaveOnServerDays() >= 1); - leaveOnServerDaysSpin->setValue(Settings::self()->leaveOnServerDays() >= 1 - ? Settings::self()->leaveOnServerDays() : 7); - leaveOnServerCountCheck->setEnabled(Settings::self()->leaveOnServer()); - leaveOnServerCountCheck->setChecked(Settings::self()->leaveOnServerCount() >= 1); - leaveOnServerCountSpin->setValue(Settings::self()->leaveOnServerCount() >= 1 - ? Settings::self()->leaveOnServerCount() : 100); - leaveOnServerSizeCheck->setEnabled(Settings::self()->leaveOnServer()); - leaveOnServerSizeCheck->setChecked(Settings::self()->leaveOnServerSize() >= 1); - leaveOnServerSizeSpin->setValue(Settings::self()->leaveOnServerSize() >= 1 - ? Settings::self()->leaveOnServerSize() : 10); - filterOnServerCheck->setChecked(Settings::self()->filterOnServer()); - filterOnServerSizeSpin->setValue(Settings::self()->filterCheckSize()); - intervalCheck->setChecked(Settings::self()->intervalCheckEnabled()); - intervalSpin->setValue(Settings::self()->intervalCheckInterval()); - intervalSpin->setEnabled(Settings::self()->intervalCheckEnabled()); - - const int authenticationMethod = Settings::self()->authenticationMethod(); + hostEdit->setText(mSettings.host()); + portEdit->setValue(mSettings.port()); + precommand->setText(mSettings.precommand()); + usePipeliningCheck->setChecked(mSettings.pipelining()); + leaveOnServerCheck->setChecked(mSettings.leaveOnServer()); + leaveOnServerDaysCheck->setEnabled(mSettings.leaveOnServer()); + leaveOnServerDaysCheck->setChecked(mSettings.leaveOnServerDays() >= 1); + leaveOnServerDaysSpin->setValue(mSettings.leaveOnServerDays() >= 1 + ? mSettings.leaveOnServerDays() : 7); + leaveOnServerCountCheck->setEnabled(mSettings.leaveOnServer()); + leaveOnServerCountCheck->setChecked(mSettings.leaveOnServerCount() >= 1); + leaveOnServerCountSpin->setValue(mSettings.leaveOnServerCount() >= 1 + ? mSettings.leaveOnServerCount() : 100); + leaveOnServerSizeCheck->setEnabled(mSettings.leaveOnServer()); + leaveOnServerSizeCheck->setChecked(mSettings.leaveOnServerSize() >= 1); + leaveOnServerSizeSpin->setValue(mSettings.leaveOnServerSize() >= 1 + ? mSettings.leaveOnServerSize() : 10); + filterOnServerCheck->setChecked(mSettings.filterOnServer()); + filterOnServerSizeSpin->setValue(mSettings.filterCheckSize()); + intervalCheck->setChecked(mSettings.intervalCheckEnabled()); + intervalSpin->setValue(mSettings.intervalCheckInterval()); + intervalSpin->setEnabled(mSettings.intervalCheckEnabled()); + + const int authenticationMethod = mSettings.authenticationMethod(); authCombo->setCurrentIndex(authCombo->findData(authenticationMethod)); - encryptionNone->setChecked(!Settings::self()->useSSL() && !Settings::self()->useTLS()); - encryptionSSL->setChecked(Settings::self()->useSSL()); - encryptionTLS->setChecked(Settings::self()->useTLS()); - proxyCheck->setChecked(Settings::self()->useProxy()); + encryptionNone->setChecked(!mSettings.useSSL() && !mSettings.useTLS()); + encryptionSSL->setChecked(mSettings.useSSL()); + encryptionTLS->setChecked(mSettings.useTLS()); + proxyCheck->setChecked(mSettings.useProxy()); slotEnableLeaveOnServerDays(leaveOnServerDaysCheck->isEnabled() - ? Settings::self()->leaveOnServerDays() >= 1 : false); + ? mSettings.leaveOnServerDays() >= 1 : false); slotEnableLeaveOnServerCount(leaveOnServerCountCheck->isEnabled() - ? Settings::self()->leaveOnServerCount() >= 1 : false); + ? mSettings.leaveOnServerCount() >= 1 : false); slotEnableLeaveOnServerSize(leaveOnServerSizeCheck->isEnabled() - ? Settings::self()->leaveOnServerSize() >= 1 : false); + ? mSettings.leaveOnServerSize() >= 1 : false); // We need to fetch the collection, as the CollectionRequester needs the name // of it to work correctly - Collection targetCollection(Settings::self()->targetCollection()); + Collection targetCollection(mSettings.targetCollection()); if (targetCollection.isValid()) { CollectionFetchJob *fetchJob = new CollectionFetchJob(targetCollection, CollectionFetchJob::Base, this); connect(fetchJob, &CollectionFetchJob::collectionsReceived, this, &AccountWidget::targetCollectionReceived); } else { // FIXME: This is a bit duplicated from POP3Resource... // No target collection set in the config? Try requesting a default inbox SpecialMailCollectionsRequestJob *requestJob = new SpecialMailCollectionsRequestJob(this); requestJob->requestDefaultCollection(SpecialMailCollections::Inbox); requestJob->start(); connect(requestJob, &SpecialMailCollectionsRequestJob::result, this, &AccountWidget::localFolderRequestJobFinished); } mWallet = Wallet::openWallet(Wallet::NetworkWallet(), winId(), Wallet::Asynchronous); if (mWallet) { connect(mWallet, &KWallet::Wallet::walletOpened, this, &AccountWidget::walletOpenedForLoading); } else { passwordEdit->lineEdit()->setPlaceholderText(i18n("Wallet disabled in system settings")); } passwordEdit->setEnabled(false); passwordLabel->setEnabled(false); } void AccountWidget::walletOpenedForLoading(bool success) { if (success) { if (mWallet->isOpen()) { passwordEdit->setEnabled(true); passwordLabel->setEnabled(true); } if (mWallet->isOpen() && mWallet->hasFolder(QStringLiteral("pop3"))) { QString password; mWallet->setFolder(QStringLiteral("pop3")); mWallet->readPassword(mIdentifier, password); passwordEdit->setPassword(password); mInitalPassword = password; } else { qCWarning(POP3RESOURCE_LOG) << "Wallet not open or doesn't have pop3 folder."; } } else { qCWarning(POP3RESOURCE_LOG) << "Failed to open wallet for loading the password."; } const bool walletError = !success || !mWallet->isOpen(); if (walletError) { passwordEdit->lineEdit()->setPlaceholderText(i18n("Unable to open wallet")); } } void AccountWidget::walletOpenedForSaving(bool success) { if (success) { if (mWallet && mWallet->isOpen()) { // Remove the password from the wallet if the user doesn't want to store it if (passwordEdit->password().isEmpty() && mWallet->hasFolder(QStringLiteral("pop3"))) { mWallet->setFolder(QStringLiteral("pop3")); mWallet->removeEntry(mIdentifier); } // Store the password in the wallet if the user wants that else if (!passwordEdit->password().isEmpty()) { if (!mWallet->hasFolder(QStringLiteral("pop3"))) { mWallet->createFolder(QStringLiteral("pop3")); } mWallet->setFolder(QStringLiteral("pop3")); mWallet->writePassword(mIdentifier, passwordEdit->password()); } } else { qCWarning(POP3RESOURCE_LOG) << "Wallet not open."; } } else { // Should we alert the user here? qCWarning(POP3RESOURCE_LOG) << "Failed to open wallet for saving the password."; } delete mWallet; mWallet = nullptr; } void AccountWidget::slotLeaveOnServerClicked() { const bool state = leaveOnServerCheck->isChecked(); leaveOnServerDaysCheck->setEnabled(state); leaveOnServerCountCheck->setEnabled(state); leaveOnServerSizeCheck->setEnabled(state); if (state) { if (leaveOnServerDaysCheck->isChecked()) { slotEnableLeaveOnServerDays(state); } if (leaveOnServerCountCheck->isChecked()) { slotEnableLeaveOnServerCount(state); } if (leaveOnServerSizeCheck->isChecked()) { slotEnableLeaveOnServerSize(state); } } else { slotEnableLeaveOnServerDays(state); slotEnableLeaveOnServerCount(state); slotEnableLeaveOnServerSize(state); } if (mServerTest && !mServerTest->capabilities().contains(ServerTest::UIDL) && leaveOnServerCheck->isChecked()) { KMessageBox::information(topLevelWidget(), i18n("The server does not seem to support unique " "message numbers, but this is a " "requirement for leaving messages on the " "server.\n" "Since some servers do not correctly " "announce their capabilities you still " "have the possibility to turn leaving " "fetched messages on the server on.")); } } void AccountWidget::slotFilterOnServerClicked() { if (mServerTest && !mServerTest->capabilities().contains(ServerTest::Top) && filterOnServerCheck->isChecked()) { KMessageBox::information(topLevelWidget(), i18n("The server does not seem to support " "fetching message headers, but this is a " "requirement for filtering messages on the " "server.\n" "Since some servers do not correctly " "announce their capabilities you still " "have the possibility to turn filtering " "messages on the server on.")); } } void AccountWidget::slotPipeliningClicked() { if (usePipeliningCheck->isChecked()) { KMessageBox::information(topLevelWidget(), i18n("Please note that this feature can cause some POP3 servers " "that do not support pipelining to send corrupted mail;\n" "this is configurable, though, because some servers support pipelining\n" "but do not announce their capabilities. To check whether your POP3 server\n" "announces pipelining support use the \"Auto Detect\"" " button at the bottom of the dialog;\n" "if your server does not announce it, but you want more speed, then\n" "you should do some testing first by sending yourself a batch\n" "of mail and downloading it."), QString(), QStringLiteral("pipelining")); } } void AccountWidget::slotPopEncryptionChanged(QAbstractButton *button) { if (button) { const int id = encryptionButtonGroup->id(button); qCDebug(POP3RESOURCE_LOG) << "setting port"; // adjust port if (id == Transport::EnumEncryption::SSL || portEdit->value() == 995) { portEdit->setValue((id == Transport::EnumEncryption::SSL) ? 995 : 110); } qCDebug(POP3RESOURCE_LOG) << "port set "; enablePopFeatures(); // removes invalid auth options from the combobox } } void AccountWidget::slotCheckPopCapabilities() { if (hostEdit->text().isEmpty()) { KMessageBox::sorry(this, i18n("Please specify a server and port on " "the General tab first.")); return; } delete mServerTest; mServerTest = new ServerTest(this); BusyCursorHelper *busyCursorHelper = new BusyCursorHelper(mServerTest); mServerTest->setProgressBar(checkCapabilitiesProgress); Q_EMIT okEnabled(false); checkCapabilitiesStack->setCurrentIndex(1); Transport::EnumEncryption::type encryptionType; if (encryptionSSL->isChecked()) { encryptionType = Transport::EnumEncryption::SSL; } else { encryptionType = Transport::EnumEncryption::None; } mServerTest->setPort(encryptionType, portEdit->value()); mServerTest->setServer(hostEdit->text()); mServerTest->setProtocol(QStringLiteral("pop")); connect(mServerTest, &MailTransport::ServerTest::finished, this, &AccountWidget::slotPopCapabilities); connect(mServerTest, &MailTransport::ServerTest::finished, busyCursorHelper, &BusyCursorHelper::deleteLater); mServerTest->start(); mServerTestFailed = false; } void AccountWidget::slotPopCapabilities(const QVector &encryptionTypes) { checkCapabilitiesStack->setCurrentIndex(0); Q_EMIT okEnabled(true); // if both fail, popup a dialog if (!mServerTest->isNormalPossible() && !mServerTest->isSecurePossible()) { KMessageBox::sorry(this, i18n("Unable to connect to the server, please verify the server address.")); } // If the servertest did not find any useable authentication modes, assume the // connection failed and don't disable any of the radioboxes. if (encryptionTypes.isEmpty()) { mServerTestFailed = true; return; } encryptionNone->setEnabled(encryptionTypes.contains(Transport::EnumEncryption::None)); encryptionSSL->setEnabled(encryptionTypes.contains(Transport::EnumEncryption::SSL)); encryptionTLS->setEnabled(encryptionTypes.contains(Transport::EnumEncryption::TLS)); usePipeliningCheck->setChecked(mServerTest->capabilities().contains(ServerTest::Pipelining)); checkHighest(encryptionButtonGroup); } void AccountWidget::enablePopFeatures() { if (!mServerTest || mServerTestFailed) { return; } QVector supportedAuths; if (encryptionButtonGroup->checkedId() == Transport::EnumEncryption::None) { supportedAuths = mServerTest->normalProtocols(); } if (encryptionButtonGroup->checkedId() == Transport::EnumEncryption::SSL) { supportedAuths = mServerTest->secureProtocols(); } if (encryptionButtonGroup->checkedId() == Transport::EnumEncryption::TLS) { supportedAuths = mServerTest->tlsProtocols(); } authCombo->clear(); for (int prot : qAsConst(supportedAuths)) { authCombo->addItem(Transport::authenticationTypeString(prot), prot); } if (mServerTest && !mServerTest->capabilities().contains(ServerTest::Pipelining) && usePipeliningCheck->isChecked()) { usePipeliningCheck->setChecked(false); KMessageBox::information(topLevelWidget(), i18n("The server does not seem to support " "pipelining; therefore, this option has " "been disabled.\n" "Since some servers do not correctly " "announce their capabilities you still " "have the possibility to turn pipelining " "on. But please note that this feature can " "cause some POP servers that do not " "support pipelining to send corrupt " "messages. So before using this feature " "with important mail you should first " "test it by sending yourself a larger " "number of test messages which you all " "download in one go from the POP " "server.")); } if (mServerTest && !mServerTest->capabilities().contains(ServerTest::UIDL) && leaveOnServerCheck->isChecked()) { leaveOnServerCheck->setChecked(false); KMessageBox::information(topLevelWidget(), i18n("The server does not seem to support unique " "message numbers, but this is a " "requirement for leaving messages on the " "server; therefore, this option has been " "disabled.\n" "Since some servers do not correctly " "announce their capabilities you still " "have the possibility to turn leaving " "fetched messages on the server on.")); } if (mServerTest && !mServerTest->capabilities().contains(ServerTest::Top) && filterOnServerCheck->isChecked()) { filterOnServerCheck->setChecked(false); KMessageBox::information(topLevelWidget(), i18n("The server does not seem to support " "fetching message headers, but this is a " "requirement for filtering messages on the " "server; therefore, this option has been " "disabled.\n" "Since some servers do not correctly " "announce their capabilities you still " "have the possibility to turn filtering " "messages on the server on.")); } } static void addAuthenticationItem(QComboBox *combo, int authenticationType) { combo->addItem(Transport::authenticationTypeString(authenticationType), QVariant(authenticationType)); } void AccountWidget::populateDefaultAuthenticationOptions() { authCombo->clear(); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::CLEAR); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::LOGIN); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::PLAIN); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::CRAM_MD5); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::DIGEST_MD5); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::NTLM); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::GSSAPI); addAuthenticationItem(authCombo, Transport::EnumAuthenticationType::APOP); } void AccountWidget::slotLeaveOnServerDaysChanged(int value) { leaveOnServerDaysSpin->setSuffix(i18np(" day", " days", value)); } void AccountWidget::slotLeaveOnServerCountChanged(int value) { leaveOnServerCountSpin->setSuffix(i18np(" message", " messages", value)); } void AccountWidget::slotFilterOnServerSizeChanged(int value) { filterOnServerSizeSpin->setSuffix(i18np(" byte", " bytes", value)); } void AccountWidget::checkHighest(QButtonGroup *btnGroup) { QListIterator it(btnGroup->buttons()); it.toBack(); while (it.hasPrevious()) { QAbstractButton *btn = it.previous(); if (btn && btn->isEnabled()) { btn->animateClick(); return; } } } void AccountWidget::slotAccepted() { saveSettings(); // Don't call accept() yet, saveSettings() triggers an asynchronous wallet operation, // which will call accept() when it is finished } void AccountWidget::saveSettings() { - Settings::self()->setName(nameEdit->text()); - Settings::self()->setIntervalCheckEnabled(intervalCheck->checkState() == Qt::Checked); - Settings::self()->setIntervalCheckInterval(intervalSpin->value()); - Settings::self()->setHost(hostEdit->text().trimmed()); - Settings::self()->setPort(portEdit->value()); - Settings::self()->setLogin(loginEdit->text().trimmed()); - Settings::self()->setPrecommand(precommand->text()); - Settings::self()->setUseSSL(encryptionSSL->isChecked()); - Settings::self()->setUseTLS(encryptionTLS->isChecked()); - Settings::self()->setAuthenticationMethod(authCombo->itemData(authCombo->currentIndex()).toInt()); - Settings::self()->setUseProxy(proxyCheck->isChecked()); - Settings::self()->setPipelining(usePipeliningCheck->isChecked()); - Settings::self()->setLeaveOnServer(leaveOnServerCheck->isChecked()); - Settings::self()->setLeaveOnServerDays(leaveOnServerCheck->isChecked() + mSettings.setName(nameEdit->text()); + mSettings.setIntervalCheckEnabled(intervalCheck->checkState() == Qt::Checked); + mSettings.setIntervalCheckInterval(intervalSpin->value()); + mSettings.setHost(hostEdit->text().trimmed()); + mSettings.setPort(portEdit->value()); + mSettings.setLogin(loginEdit->text().trimmed()); + mSettings.setPrecommand(precommand->text()); + mSettings.setUseSSL(encryptionSSL->isChecked()); + mSettings.setUseTLS(encryptionTLS->isChecked()); + mSettings.setAuthenticationMethod(authCombo->itemData(authCombo->currentIndex()).toInt()); + mSettings.setUseProxy(proxyCheck->isChecked()); + mSettings.setPipelining(usePipeliningCheck->isChecked()); + mSettings.setLeaveOnServer(leaveOnServerCheck->isChecked()); + mSettings.setLeaveOnServerDays(leaveOnServerCheck->isChecked() ? (leaveOnServerDaysCheck->isChecked() ? leaveOnServerDaysSpin->value() : -1) : 0); - Settings::self()->setLeaveOnServerCount(leaveOnServerCheck->isChecked() + mSettings.setLeaveOnServerCount(leaveOnServerCheck->isChecked() ? (leaveOnServerCountCheck->isChecked() ? leaveOnServerCountSpin->value() : -1) : 0); - Settings::self()->setLeaveOnServerSize(leaveOnServerCheck->isChecked() + mSettings.setLeaveOnServerSize(leaveOnServerCheck->isChecked() ? (leaveOnServerSizeCheck->isChecked() ? leaveOnServerSizeSpin->value() : -1) : 0); - Settings::self()->setFilterOnServer(filterOnServerCheck->isChecked()); - Settings::self()->setFilterCheckSize(filterOnServerSizeSpin->value()); - Settings::self()->setTargetCollection(folderRequester->collection().id()); - Settings::self()->save(); + mSettings.setFilterOnServer(filterOnServerCheck->isChecked()); + mSettings.setFilterCheckSize(filterOnServerSizeSpin->value()); + mSettings.setTargetCollection(folderRequester->collection().id()); + mSettings.save(); // Now, either save the password or delete it from the wallet. For both, we need // to open it. const bool userChangedPassword = mInitalPassword != passwordEdit->password(); const bool userWantsToDeletePassword = passwordEdit->password().isEmpty() && userChangedPassword; if ((!passwordEdit->password().isEmpty() && userChangedPassword) || userWantsToDeletePassword) { qCDebug(POP3RESOURCE_LOG) << mWallet << mWallet->isOpen(); if (mWallet && mWallet->isOpen()) { // wallet is already open walletOpenedForSaving(true); } else { // we need to open the wallet qCDebug(POP3RESOURCE_LOG) << "we need to open the wallet"; mWallet = Wallet::openWallet(Wallet::NetworkWallet(), winId(), Wallet::Synchronous); if (mWallet) { walletOpenedForSaving(true); } } } } void AccountWidget::slotEnableLeaveOnServerDays(bool state) { if (state && !leaveOnServerDaysCheck->isEnabled()) { return; } leaveOnServerDaysSpin->setEnabled(state); } void AccountWidget::slotEnableLeaveOnServerCount(bool state) { if (state && !leaveOnServerCountCheck->isEnabled()) { return; } leaveOnServerCountSpin->setEnabled(state); } void AccountWidget::slotEnableLeaveOnServerSize(bool state) { if (state && !leaveOnServerSizeCheck->isEnabled()) { return; } leaveOnServerSizeSpin->setEnabled(state); } void AccountWidget::slotEnablePopInterval(bool state) { intervalSpin->setEnabled(state); intervalLabel->setEnabled(state); } void AccountWidget::targetCollectionReceived(Akonadi::Collection::List collections) { folderRequester->setCollection(collections.first()); } void AccountWidget::localFolderRequestJobFinished(KJob *job) { if (!job->error()) { Collection targetCollection = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Inbox); Q_ASSERT(targetCollection.isValid()); folderRequester->setCollection(targetCollection); } } diff --git a/resources/pop3/accountwidget.h b/resources/pop3/accountwidget.h index 64fd974b1..3294ca4e8 100644 --- a/resources/pop3/accountwidget.h +++ b/resources/pop3/accountwidget.h @@ -1,87 +1,90 @@ /* * Copyright (C) 2000 Espen Sand, espen@kde.org * Copyright 2009 Thomas McGuire * Copyright (c) 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company * Copyright (C) 2010 Casey Link * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef ACCOUNT_WIDGET_H #define ACCOUNT_WIDGET_H #include "ui_popsettings.h" +class Settings; + namespace MailTransport { class ServerTest; } namespace KWallet { class Wallet; } class KJob; class AccountWidget : public QWidget, private Ui::PopPage { Q_OBJECT public: - AccountWidget(const QString &identifier, QWidget *parent); + AccountWidget(Settings &settings, const QString &identifier, QWidget *parent); ~AccountWidget() override; void loadSettings(); void saveSettings(); Q_SIGNALS: void okEnabled(bool enabled); private Q_SLOTS: void slotEnablePopInterval(bool state); void slotLeaveOnServerClicked(); void slotEnableLeaveOnServerDays(bool state); void slotEnableLeaveOnServerCount(bool state); void slotEnableLeaveOnServerSize(bool state); void slotFilterOnServerClicked(); void slotPipeliningClicked(); void slotPopEncryptionChanged(QAbstractButton *button); void slotCheckPopCapabilities(); void slotPopCapabilities(const QVector &); void slotLeaveOnServerDaysChanged(int value); void slotLeaveOnServerCountChanged(int value); void slotFilterOnServerSizeChanged(int value); void targetCollectionReceived(Akonadi::Collection::List collections); void localFolderRequestJobFinished(KJob *job); void walletOpenedForLoading(bool success); void walletOpenedForSaving(bool success); void slotAccepted(); private: void setupWidgets(); void checkHighest(QButtonGroup *); void enablePopFeatures(); void populateDefaultAuthenticationOptions(); private: QButtonGroup *encryptionButtonGroup = nullptr; MailTransport::ServerTest *mServerTest = nullptr; QRegExpValidator mValidator; bool mServerTestFailed = false; KWallet::Wallet *mWallet = nullptr; QString mInitalPassword; QString mIdentifier; + Settings &mSettings; }; #endif diff --git a/resources/pop3/jobs.cpp b/resources/pop3/jobs.cpp index 43f3f3883..2d32b72b8 100644 --- a/resources/pop3/jobs.cpp +++ b/resources/pop3/jobs.cpp @@ -1,504 +1,505 @@ /* Copyright 2009 Thomas McGuire 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "jobs.h" #include "settings.h" #include #include #include #include #include #include "pop3resource_debug.h" #include -POPSession::POPSession(const QString &password) +POPSession::POPSession(Settings &settings, const QString &password) : mCurrentJob(nullptr) , mPassword(password) + , mSettings(settings) { KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave*,int,QString)), this, SLOT(slotSlaveError(KIO::Slave*,int,QString))); } POPSession::~POPSession() { closeSession(); } void POPSession::slotSlaveError(KIO::Slave *slave, int errorCode, const QString &errorMessage) { Q_UNUSED(slave); qCWarning(POP3RESOURCE_LOG) << "Got a slave error:" << errorMessage; if (slave != mSlave) { return; } if (errorCode == KIO::ERR_SLAVE_DIED) { mSlave = nullptr; } // Explicitly disconnect the slave if the connection went down if (errorCode == KIO::ERR_CONNECTION_BROKEN && mSlave) { KIO::Scheduler::disconnectSlave(mSlave); mSlave = nullptr; } if (!mCurrentJob) { Q_EMIT slaveError(errorCode, errorMessage); } else { // Let the job deal with the problem mCurrentJob->slaveError(errorCode, errorMessage); } } void POPSession::setCurrentJob(SlaveBaseJob *job) { mCurrentJob = job; } KIO::MetaData POPSession::slaveConfig() const { KIO::MetaData m; m.insert(QStringLiteral("progress"), QStringLiteral("off")); - m.insert(QStringLiteral("tls"), Settings::self()->useTLS() ? QStringLiteral("on") : QStringLiteral("off")); - m.insert(QStringLiteral("pipelining"), (Settings::self()->pipelining()) ? QStringLiteral("on") : QStringLiteral("off")); - m.insert(QStringLiteral("useProxy"), Settings::self()->useProxy() ? QStringLiteral("on") : QStringLiteral("off")); - int type = Settings::self()->authenticationMethod(); + m.insert(QStringLiteral("tls"), mSettings.useTLS() ? QStringLiteral("on") : QStringLiteral("off")); + m.insert(QStringLiteral("pipelining"), (mSettings.pipelining()) ? QStringLiteral("on") : QStringLiteral("off")); + m.insert(QStringLiteral("useProxy"), mSettings.useProxy() ? QStringLiteral("on") : QStringLiteral("off")); + int type = mSettings.authenticationMethod(); switch (type) { case MailTransport::Transport::EnumAuthenticationType::PLAIN: case MailTransport::Transport::EnumAuthenticationType::LOGIN: case MailTransport::Transport::EnumAuthenticationType::CRAM_MD5: case MailTransport::Transport::EnumAuthenticationType::DIGEST_MD5: case MailTransport::Transport::EnumAuthenticationType::NTLM: case MailTransport::Transport::EnumAuthenticationType::GSSAPI: m.insert(QStringLiteral("auth"), QStringLiteral("SASL")); m.insert(QStringLiteral("sasl"), authenticationToString(type)); break; case MailTransport::Transport::EnumAuthenticationType::CLEAR: m.insert(QStringLiteral("auth"), QStringLiteral("USER")); break; default: m.insert(QStringLiteral("auth"), authenticationToString(type)); break; } return m; } QString POPSession::authenticationToString(int type) const { switch (type) { case MailTransport::Transport::EnumAuthenticationType::LOGIN: return QStringLiteral("LOGIN"); case MailTransport::Transport::EnumAuthenticationType::PLAIN: return QStringLiteral("PLAIN"); case MailTransport::Transport::EnumAuthenticationType::CRAM_MD5: return QStringLiteral("CRAM-MD5"); case MailTransport::Transport::EnumAuthenticationType::DIGEST_MD5: return QStringLiteral("DIGEST-MD5"); case MailTransport::Transport::EnumAuthenticationType::GSSAPI: return QStringLiteral("GSSAPI"); case MailTransport::Transport::EnumAuthenticationType::NTLM: return QStringLiteral("NTLM"); case MailTransport::Transport::EnumAuthenticationType::CLEAR: return QStringLiteral("USER"); case MailTransport::Transport::EnumAuthenticationType::APOP: return QStringLiteral("APOP"); default: break; } return QString(); } QUrl POPSession::getUrl() const { QUrl url; - if (Settings::self()->useSSL()) { + if (mSettings.useSSL()) { url.setScheme(QStringLiteral("pop3s")); } else { url.setScheme(QStringLiteral("pop3")); } - url.setUserName(Settings::self()->login()); + url.setUserName(mSettings.login()); url.setPassword(mPassword); - url.setHost(Settings::self()->host()); - url.setPort(Settings::self()->port()); + url.setHost(mSettings.host()); + url.setPort(mSettings.port()); return url; } bool POPSession::connectSlave() { mSlave = KIO::Scheduler::getConnectedSlave(getUrl(), slaveConfig()); return mSlave != nullptr; } void POPSession::abortCurrentJob() { if (mCurrentJob) { mCurrentJob->kill(KJob::Quietly); mCurrentJob = nullptr; } } void POPSession::closeSession() { if (mSlave) { KIO::Scheduler::disconnectSlave(mSlave); } } KIO::Slave *POPSession::getSlave() const { return mSlave; } static QByteArray cleanupListRespone(const QByteArray &response) { QByteArray ret = response.simplified(); // Workaround for Maillennium POP3/UNIBOX // Get rid of the null terminating character, if it exists int retSize = ret.size(); if (retSize > 0 && ret.at(retSize - 1) == 0) { ret.chop(1); } return ret; } static QString intListToString(const QList &intList) { QString idList; for (int id : intList) { idList += QString::number(id) + QLatin1Char(','); } idList.chop(1); return idList; } SlaveBaseJob::SlaveBaseJob(POPSession *POPSession) : mJob(nullptr) , mPOPSession(POPSession) { mPOPSession->setCurrentJob(this); } SlaveBaseJob::~SlaveBaseJob() { // Don't do that here, the job might be destroyed after another one was started // and therefore overwrite the current job //mPOPSession->setCurrentJob( 0 ); } bool SlaveBaseJob::doKill() { if (mJob) { return mJob->kill(); } else { return KJob::doKill(); } } void SlaveBaseJob::slotSlaveResult(KJob *job) { mPOPSession->setCurrentJob(nullptr); if (job->error()) { setError(job->error()); setErrorText(job->errorText()); } emitResult(); mJob = nullptr; } void SlaveBaseJob::slotSlaveData(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job); qCWarning(POP3RESOURCE_LOG) << "Got unexpected slave data:" << data.data(); } void SlaveBaseJob::slaveError(int errorCode, const QString &errorMessage) { // The slave experienced some problem while running our job. // Just treat this as an error. // Derived jobs can do something more sophisticated here setError(errorCode); setErrorText(errorMessage); emitResult(); mJob = nullptr; } void SlaveBaseJob::connectJob() { connect(mJob, &KIO::TransferJob::data, this, &SlaveBaseJob::slotSlaveData); connect(mJob, &KIO::TransferJob::result, this, &SlaveBaseJob::slotSlaveResult); } void SlaveBaseJob::startJob(const QString &path) { QUrl url = mPOPSession->getUrl(); url.setPath(path); mJob = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo); KIO::Scheduler::assignJobToSlave(mPOPSession->getSlave(), mJob); connectJob(); } QString SlaveBaseJob::errorString() const { if (mJob) { return mJob->errorString(); } else { return KJob::errorString(); } } LoginJob::LoginJob(POPSession *popSession) : SlaveBaseJob(popSession) { } void LoginJob::start() { // This will create a connected slave, which means it will also try to login. KIO::Scheduler::connect(SIGNAL(slaveConnected(KIO::Slave*)), this, SLOT(slaveConnected(KIO::Slave*))); if (!mPOPSession->connectSlave()) { setError(KJob::UserDefinedError); setErrorText(i18n("Unable to create POP3 slave, aborting mail check.")); emitResult(); } } void LoginJob::slaveConnected(KIO::Slave *slave) { if (slave != mPOPSession->getSlave()) { // Odd, not our slave... return; } // Yeah it connected, so login was sucessful! emitResult(); } void LoginJob::slaveError(int errorCode, const QString &errorMessage) { setError(errorCode); setErrorText(errorMessage); mErrorString = KIO::buildErrorString(errorCode, errorMessage); emitResult(); } QString LoginJob::errorString() const { return mErrorString; } ListJob::ListJob(POPSession *popSession) : SlaveBaseJob(popSession) { } void ListJob::start() { startJob(QStringLiteral("/index")); } void ListJob::slotSlaveData(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job); // Silly slave, why are you sending us empty data? if (data.isEmpty()) { return; } QByteArray cleanData = cleanupListRespone(data); const int space = cleanData.indexOf(' '); if (space > 0) { QByteArray lengthString = cleanData.mid(space + 1); const int spaceInLengthPos = lengthString.indexOf(' '); if (spaceInLengthPos != -1) { lengthString.truncate(spaceInLengthPos); } const int length = lengthString.toInt(); QByteArray idString = cleanData.left(space); bool idIsNumber; int id = QString::fromLatin1(idString).toInt(&idIsNumber); if (idIsNumber) { mIdList.insert(id, length); } else { qCWarning(POP3RESOURCE_LOG) << "Got non-integer ID as part of the LIST response, ignoring" << idString.data(); } } else { qCWarning(POP3RESOURCE_LOG) << "Got invalid LIST response:" << data.data(); } } QMap ListJob::idList() const { return mIdList; } UIDListJob::UIDListJob(POPSession *popSession) : SlaveBaseJob(popSession) { } void UIDListJob::start() { startJob(QStringLiteral("/uidl")); } void UIDListJob::slotSlaveData(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job); // Silly slave, why are you sending us empty data? if (data.isEmpty()) { return; } QByteArray cleanData = cleanupListRespone(data); const int space = cleanData.indexOf(' '); if (space <= 0) { qCWarning(POP3RESOURCE_LOG) << "Invalid response to the UIDL command:" << data.data(); qCWarning(POP3RESOURCE_LOG) << "Ignoring this entry."; } else { QByteArray idString = cleanData.left(space); QByteArray uidString = cleanData.mid(space + 1); bool idIsNumber; int id = QString::fromLatin1(idString).toInt(&idIsNumber); if (idIsNumber) { const QString uidQString = QString::fromLatin1(uidString); if (!uidQString.isEmpty()) { mUidList.insert(id, uidQString); mIdList.insert(uidQString, id); } else { qCWarning(POP3RESOURCE_LOG) << "Got invalid/empty UID from the UIDL command:" << uidString.data(); qCWarning(POP3RESOURCE_LOG) << "The whole response was:" << data.data(); } } else { qCWarning(POP3RESOURCE_LOG) << "Got invalid ID from the UIDL command:" << idString.data(); qCWarning(POP3RESOURCE_LOG) << "The whole response was:" << data.data(); } } } QMap UIDListJob::uidList() const { return mUidList; } QMap UIDListJob::idList() const { return mIdList; } DeleteJob::DeleteJob(POPSession *popSession) : SlaveBaseJob(popSession) { } void DeleteJob::setDeleteIds(const QList &ids) { mIdsToDelete = ids; } void DeleteJob::start() { qCDebug(POP3RESOURCE_LOG) << "================= DeleteJob::start. ============================="; startJob(QLatin1String("/remove/") + intListToString(mIdsToDelete)); } QList DeleteJob::deletedIDs() const { // FIXME : The slave doesn't tell us which of the IDs were actually deleted, we // just assume all of them here return mIdsToDelete; } QuitJob::QuitJob(POPSession *popSession) : SlaveBaseJob(popSession) { } void QuitJob::start() { startJob(QStringLiteral("/commit")); } FetchJob::FetchJob(POPSession *session) : SlaveBaseJob(session) , mBytesDownloaded(0) , mTotalBytesToDownload(0) , mDataCounter(0) { } void FetchJob::setFetchIds(const QList &ids, const QList &sizes) { mIdsPendingDownload = ids; for (int size : qAsConst(sizes)) { mTotalBytesToDownload += size; } } void FetchJob::start() { startJob(QLatin1String("/download/") + intListToString(mIdsPendingDownload)); setTotalAmount(KJob::Bytes, mTotalBytesToDownload); } void FetchJob::connectJob() { SlaveBaseJob::connectJob(); connect(mJob, &KIO::TransferJob::infoMessage, this, &FetchJob::slotInfoMessage); } void FetchJob::slotSlaveData(KIO::Job *job, const QByteArray &data) { Q_UNUSED(job); mCurrentMessage += data; mBytesDownloaded += data.size(); mDataCounter++; if (mDataCounter % 5 == 0) { setProcessedAmount(KJob::Bytes, mBytesDownloaded); } } void FetchJob::slotInfoMessage(KJob *job, const QString &infoMessage, const QString &) { Q_UNUSED(job); if (infoMessage != QLatin1String("message complete")) { return; } KMime::Message::Ptr msg(new KMime::Message); msg->setContent(KMime::CRLFtoLF(mCurrentMessage)); msg->parse(); mCurrentMessage.clear(); const int idOfCurrentMessage = mIdsPendingDownload.takeFirst(); Q_EMIT messageFinished(idOfCurrentMessage, msg); } diff --git a/resources/pop3/jobs.h b/resources/pop3/jobs.h index a47a41de9..80e494e72 100644 --- a/resources/pop3/jobs.h +++ b/resources/pop3/jobs.h @@ -1,199 +1,201 @@ /* Copyright 2009 Thomas McGuire 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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. */ #ifndef JOBS_H #define JOBS_H #include #include #include #include #include #include namespace KIO { class Slave; class Job; class TransferJob; } class SlaveBaseJob; +class Settings; class POPSession : public QObject { Q_OBJECT public: - explicit POPSession(const QString &password); + explicit POPSession(Settings &settings, const QString &password); ~POPSession(); bool connectSlave(); void abortCurrentJob(); void closeSession(); KIO::Slave *getSlave() const; QUrl getUrl() const; // Sets the current SlaveBaseJob that is using the POPSession. // If there is a job, all slave errors will be forwared to that job void setCurrentJob(SlaveBaseJob *job); private Q_SLOTS: void slotSlaveError(KIO::Slave *slave, int, const QString &); Q_SIGNALS: // An error occurred within the slave. If there is a current job, this // signal is not emitted, as the job deals with it. void slaveError(int errorCode, const QString &errorMessage); private: KIO::MetaData slaveConfig() const; QString authenticationToString(int type) const; QPointer mSlave; SlaveBaseJob *mCurrentJob = nullptr; QString mPassword; + Settings &mSettings; }; class SlaveBaseJob : public KJob { Q_OBJECT public: explicit SlaveBaseJob(POPSession *POPSession); ~SlaveBaseJob(); virtual void slaveError(int errorCode, const QString &errorMessage); protected Q_SLOTS: virtual void slotSlaveData(KIO::Job *job, const QByteArray &data); virtual void slotSlaveResult(KJob *job); protected: QString errorString() const override; bool doKill() override; void startJob(const QString &path); virtual void connectJob(); KIO::TransferJob *mJob = nullptr; POPSession *mPOPSession = nullptr; }; class LoginJob : public SlaveBaseJob { Q_OBJECT public: LoginJob(POPSession *popSession); void start() override; protected: QString errorString() const override; private Q_SLOTS: void slaveConnected(KIO::Slave *slave); private: void slaveError(int errorCode, const QString &errorMessage) override; QString mErrorString; }; class ListJob : public SlaveBaseJob { Q_OBJECT public: ListJob(POPSession *popSession); QMap idList() const; void start() override; private: void slotSlaveData(KIO::Job *job, const QByteArray &data) override; private: QMap mIdList; }; class UIDListJob : public SlaveBaseJob { Q_OBJECT public: UIDListJob(POPSession *popSession); QMap uidList() const; QMap idList() const; void start() override; private: void slotSlaveData(KIO::Job *job, const QByteArray &data) override; QMap mUidList; QMap mIdList; }; class DeleteJob : public SlaveBaseJob { Q_OBJECT public: DeleteJob(POPSession *popSession); void setDeleteIds(const QList &ids); void start() override; QList deletedIDs() const; private: QList mIdsToDelete; }; class QuitJob : public SlaveBaseJob { Q_OBJECT public: QuitJob(POPSession *popSession); void start() override; }; class FetchJob : public SlaveBaseJob { Q_OBJECT public: FetchJob(POPSession *session); void setFetchIds(const QList &ids, const QList &sizes); void start() override; private Q_SLOTS: void slotInfoMessage(KJob *job, const QString &infoMessage, const QString &); Q_SIGNALS: void messageFinished(int id, KMime::Message::Ptr message); private: void connectJob() override; void slotSlaveData(KIO::Job *job, const QByteArray &data) override; QList mIdsPendingDownload; QByteArray mCurrentMessage; int mBytesDownloaded; int mTotalBytesToDownload; uint mDataCounter; }; #endif // JOBS_H diff --git a/resources/pop3/pop3config.cpp b/resources/pop3/pop3config.cpp index 8ae7b9455..83e4afab0 100644 --- a/resources/pop3/pop3config.cpp +++ b/resources/pop3/pop3config.cpp @@ -1,55 +1,55 @@ /* Copyright 2018 Daniel Vrátil 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 -#include "settings.h" #include "accountwidget.h" +#include "settings.h" class Pop3Config : public Akonadi::AgentConfigurationBase { Q_OBJECT public: Pop3Config(const KSharedConfigPtr &config, QWidget *parent, const QVariantList &args) : Akonadi::AgentConfigurationBase(config, parent, args) - , mSettings(new Settings(config)) - , mWidget(new AccountWidget(identifier(), parent)) + , mSettings(config, Settings::Option::NoOption) + , mWidget(mSettings, identifier(), parent) { - connect(mWidget.data(), &AccountWidget::okEnabled, this, &Akonadi::AgentConfigurationBase::enableOkButton); + connect(&mWidget, &AccountWidget::okEnabled, this, &Akonadi::AgentConfigurationBase::enableOkButton); } void load() override { Akonadi::AgentConfigurationBase::load(); - mWidget->loadSettings(); + mWidget.loadSettings(); } bool save() const override { - mWidget->saveSettings(); + const_cast(this)->mWidget.saveSettings(); return Akonadi::AgentConfigurationBase::save(); } - QScopedPointer mSettings; - QScopedPointer mWidget; + Settings mSettings; + AccountWidget mWidget; }; AKONADI_AGENTCONFIG_FACTORY(Pop3ConfigFactory, "pop3config.json", Pop3Config) #include "pop3config.moc" diff --git a/resources/pop3/pop3resource.cpp b/resources/pop3/pop3resource.cpp index 0d0f030fa..384bf7fef 100644 --- a/resources/pop3/pop3resource.cpp +++ b/resources/pop3/pop3resource.cpp @@ -1,1050 +1,1051 @@ /* Copyright 2009 Thomas McGuire 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "pop3resource.h" #include "settings.h" #include "jobs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pop3resource_debug.h" #include using namespace Akonadi; using namespace MailTransport; using namespace KWallet; POP3Resource::POP3Resource(const QString &id) : ResourceBase(id) , mState(Idle) , mIntervalTimer(new QTimer(this)) + , mSettings(KSharedConfig::openConfig()) + { - new Settings(KSharedConfig::openConfig()); Akonadi::AttributeFactory::registerAttribute(); setNeedsNetwork(true); - Settings::self()->setResourceId(identifier()); - if (Settings::self()->name().isEmpty()) { + mSettings.setResourceId(identifier()); + if (mSettings.name().isEmpty()) { if (name() == identifier()) { - Settings::self()->setName(i18n("POP3 Account")); + mSettings.setName(i18n("POP3 Account")); } else { - Settings::self()->setName(name()); + mSettings.setName(name()); } } - setName(Settings::self()->name()); + setName(mSettings.name()); resetState(); connect(this, &POP3Resource::abortRequested, this, &POP3Resource::slotAbortRequested); connect(mIntervalTimer, &QTimer::timeout, this, &POP3Resource::intervalCheckTriggered); connect(this, &POP3Resource::reloadConfiguration, this, &POP3Resource::configurationChanged); } POP3Resource::~POP3Resource() { - Settings::self()->save(); + mSettings.save(); delete mWallet; mWallet = nullptr; } void POP3Resource::configurationChanged() { updateIntervalTimer(); mPassword.clear(); } void POP3Resource::updateIntervalTimer() { - if (Settings::self()->intervalCheckEnabled() && mState == Idle) { - mIntervalTimer->start(Settings::self()->intervalCheckInterval() * 1000 * 60); + if (mSettings.intervalCheckEnabled() && mState == Idle) { + mIntervalTimer->start(mSettings.intervalCheckInterval() * 1000 * 60); } else { mIntervalTimer->stop(); } } void POP3Resource::intervalCheckTriggered() { Q_ASSERT(mState == Idle); if (isOnline()) { qCDebug(POP3RESOURCE_LOG) << "Starting interval mail check."; startMailCheck(); mIntervalCheckInProgress = true; } else { mIntervalTimer->start(); } } void POP3Resource::aboutToQuit() { if (mState != Idle) { cancelSync(i18n("Mail check aborted.")); } } void POP3Resource::slotAbortRequested() { if (mState != Idle) { cancelSync(i18n("Mail check was canceled manually."), false /* no error */); } } void POP3Resource::retrieveItems(const Akonadi::Collection &collection) { Q_UNUSED(collection); qCWarning(POP3RESOURCE_LOG) << "This should never be called, we don't have a collection!"; } bool POP3Resource::retrieveItem(const Akonadi::Item &item, const QSet &parts) { Q_UNUSED(item); Q_UNUSED(parts); qCWarning(POP3RESOURCE_LOG) << "This should never be called, we don't have any item!"; return false; } QString POP3Resource::buildLabelForPasswordDialog(const QString &detailedError) const { const QString queryText = i18n("Please enter the username and password for account '%1'.", agentName()) + QLatin1String("
") + detailedError; return queryText; } void POP3Resource::walletOpenedForLoading(bool success) { bool passwordLoaded = success; if (success) { if (mWallet && mWallet->isOpen() && mWallet->hasFolder(QStringLiteral("pop3"))) { mWallet->setFolder(QStringLiteral("pop3")); if (mWallet->hasEntry(identifier())) { mWallet->readPassword(identifier(), mPassword); } else { passwordLoaded = false; } } else { passwordLoaded = false; } } delete mWallet; mWallet = nullptr; if (!passwordLoaded) { const QString queryText = buildLabelForPasswordDialog( i18n("You are asked here because the password could not be loaded from the wallet.")); showPasswordDialog(queryText); } else { advanceState(Connect); } } void POP3Resource::walletOpenedForSaving(bool success) { if (success) { if (mWallet && mWallet->isOpen()) { if (!mWallet->hasFolder(QStringLiteral("pop3"))) { mWallet->createFolder(QStringLiteral("pop3")); } mWallet->setFolder(QStringLiteral("pop3")); mWallet->writePassword(identifier(), mPassword); } } else { qCWarning(POP3RESOURCE_LOG) << "Unable to write the password to the wallet."; } delete mWallet; mWallet = nullptr; finish(); } void POP3Resource::showPasswordDialog(const QString &queryText) { QPointer dlg = new KPasswordDialog( nullptr, KPasswordDialog::ShowUsernameLine); dlg->setModal(true); - dlg->setUsername(Settings::self()->login()); + dlg->setUsername(mSettings.login()); dlg->setPassword(mPassword); dlg->setPrompt(queryText); dlg->setWindowTitle(name()); dlg->addCommentLine(i18n("Account:"), name()); bool gotIt = false; if (dlg->exec()) { mPassword = dlg->password(); - Settings::self()->setLogin(dlg->username()); - Settings::self()->save(); + mSettings.setLogin(dlg->username()); + mSettings.save(); if (!dlg->password().isEmpty()) { mSavePassword = true; } mAskAgain = false; advanceState(Connect); gotIt = true; } delete dlg; if (!gotIt) { cancelSync(i18n("No username and password supplied.")); } } void POP3Resource::advanceState(State nextState) { mState = nextState; doStateStep(); } void POP3Resource::doStateStep() { switch (mState) { case Idle: Q_ASSERT(false); qCWarning(POP3RESOURCE_LOG) << "State machine should not be called in idle state!"; break; case FetchTargetCollection: { qCDebug(POP3RESOURCE_LOG) << "================ Starting state FetchTargetCollection =========="; Q_EMIT status(Running, i18n("Preparing transmission from \"%1\".", name())); - Collection targetCollection(Settings::self()->targetCollection()); + Collection targetCollection(mSettings.targetCollection()); if (targetCollection.isValid()) { CollectionFetchJob *fetchJob = new CollectionFetchJob(targetCollection, CollectionFetchJob::Base); fetchJob->start(); connect(fetchJob, &CollectionFetchJob::result, this, &POP3Resource::targetCollectionFetchJobFinished); } else { // No target collection set in the config? Try requesting a default inbox SpecialMailCollectionsRequestJob *requestJob = new SpecialMailCollectionsRequestJob(this); requestJob->requestDefaultCollection(SpecialMailCollections::Inbox); requestJob->start(); connect(requestJob, &SpecialMailCollectionsRequestJob::result, this, &POP3Resource::localFolderRequestJobFinished); } break; } case Precommand: qCDebug(POP3RESOURCE_LOG) << "================ Starting state Precommand ====================="; - if (!Settings::self()->precommand().isEmpty()) { - PrecommandJob *precommandJob = new PrecommandJob(Settings::self()->precommand(), this); + if (!mSettings.precommand().isEmpty()) { + PrecommandJob *precommandJob = new PrecommandJob(mSettings.precommand(), this); connect(precommandJob, &PrecommandJob::result, this, &POP3Resource::precommandResult); precommandJob->start(); Q_EMIT status(Running, i18n("Executing precommand.")); } else { advanceState(RequestPassword); } break; case RequestPassword: { qCDebug(POP3RESOURCE_LOG) << "================ Starting state RequestPassword ================"; // Don't show any wallet or password prompts when we are unit-testing - if (!Settings::self()->unitTestPassword().isEmpty()) { - mPassword = Settings::self()->unitTestPassword(); + if (!mSettings.unitTestPassword().isEmpty()) { + mPassword = mSettings.unitTestPassword(); advanceState(Connect); break; } - const bool passwordNeeded = Settings::self()->authenticationMethod() != MailTransport::Transport::EnumAuthenticationType::GSSAPI; - const bool loadPasswordFromWallet = !mAskAgain && passwordNeeded && !Settings::self()->login().isEmpty() + const bool passwordNeeded = mSettings.authenticationMethod() != MailTransport::Transport::EnumAuthenticationType::GSSAPI; + const bool loadPasswordFromWallet = !mAskAgain && passwordNeeded && !mSettings.login().isEmpty() && mPassword.isEmpty(); if (loadPasswordFromWallet) { mWallet = Wallet::openWallet(Wallet::NetworkWallet(), winIdForDialogs(), Wallet::Asynchronous); } if (loadPasswordFromWallet && mWallet) { connect(mWallet, &KWallet::Wallet::walletOpened, this, &POP3Resource::walletOpenedForLoading); } else if (passwordNeeded && (mPassword.isEmpty() || mAskAgain)) { QString detail; if (mAskAgain) { detail = i18n("You are asked here because the previous login was not successful."); - } else if (Settings::self()->login().isEmpty()) { + } else if (mSettings.login().isEmpty()) { detail = i18n("You are asked here because the username you supplied is empty."); } else if (!mWallet) { detail = i18n("You are asked here because the wallet password storage is disabled."); } showPasswordDialog(buildLabelForPasswordDialog(detail)); } else { // No password needed or using previous password, go on with Connect advanceState(Connect); } break; } case Connect: qCDebug(POP3RESOURCE_LOG) << "================ Starting state Connect ========================"; Q_ASSERT(!mPopSession); - mPopSession = new POPSession(mPassword); + mPopSession = new POPSession(mSettings, mPassword); connect(mPopSession, &POPSession::slaveError, this, &POP3Resource::slotSessionError); advanceState(Login); break; case Login: { qCDebug(POP3RESOURCE_LOG) << "================ Starting state Login =========================="; LoginJob *loginJob = new LoginJob(mPopSession); connect(loginJob, &LoginJob::result, this, &POP3Resource::loginJobResult); loginJob->start(); break; } case List: { qCDebug(POP3RESOURCE_LOG) << "================ Starting state List ==========================="; Q_EMIT status(Running, i18n("Fetching mail listing.")); ListJob *listJob = new ListJob(mPopSession); connect(listJob, &ListJob::result, this, &POP3Resource::listJobResult); listJob->start(); break; } case UIDList: { qCDebug(POP3RESOURCE_LOG) << "================ Starting state UIDList ========================"; UIDListJob *uidListJob = new UIDListJob(mPopSession); connect(uidListJob, &UIDListJob::result, this, &POP3Resource::uidListJobResult); uidListJob->start(); break; } case Download: { qCDebug(POP3RESOURCE_LOG) << "================ Starting state Download ======================="; // Determine which mails we want to download. Those are all mails which are // currently on ther server, minus the ones we have already downloaded (we // remember which UIDs we have downloaded in the settings) QList idsToDownload = mIdsToSizeMap.keys(); - const QStringList alreadyDownloadedUIDs = Settings::self()->seenUidList(); + const QStringList alreadyDownloadedUIDs = mSettings.seenUidList(); foreach (const QString &uidOnServer, mIdsToUidsMap) { if (alreadyDownloadedUIDs.contains(uidOnServer)) { const int idOfUIDOnServer = mUidsToIdsMap.value(uidOnServer, -1); Q_ASSERT(idOfUIDOnServer != -1); idsToDownload.removeAll(idOfUIDOnServer); } } mIdsToDownload = idsToDownload; qCDebug(POP3RESOURCE_LOG) << "We are going to download" << mIdsToDownload.size() << "messages"; // For proper progress, the job needs to know the sizes of the messages, so // put them into a list here QList sizesOfMessagesToDownload; sizesOfMessagesToDownload.reserve(idsToDownload.count()); foreach (int id, idsToDownload) { sizesOfMessagesToDownload.append(mIdsToSizeMap.value(id)); } if (mIdsToDownload.empty()) { advanceState(CheckRemovingMessage); } else { FetchJob *fetchJob = new FetchJob(mPopSession); fetchJob->setFetchIds(idsToDownload, sizesOfMessagesToDownload); connect(fetchJob, &FetchJob::result, this, &POP3Resource::fetchJobResult); connect(fetchJob, &FetchJob::messageFinished, this, &POP3Resource::messageFinished); //TODO wait kf6. For the moment we can't convert to new connect api. connect(fetchJob, SIGNAL(processedAmount(KJob*,KJob::Unit,qulonglong)), this, SLOT(messageDownloadProgress(KJob*,KJob::Unit,qulonglong))); fetchJob->start(); } break; } case Save: qCDebug(POP3RESOURCE_LOG) << "================ Starting state Save ==========================="; qCDebug(POP3RESOURCE_LOG) << mPendingCreateJobs.size() << "item create jobs are pending"; if (!mPendingCreateJobs.isEmpty()) { Q_EMIT status(Running, i18n("Saving downloaded messages.")); } if (shouldAdvanceToQuitState()) { advanceState(Quit); } break; case Quit: { qCDebug(POP3RESOURCE_LOG) << "================ Starting state Quit ==========================="; QuitJob *quitJob = new QuitJob(mPopSession); connect(quitJob, &QuitJob::result, this, &POP3Resource::quitJobResult); quitJob->start(); break; } case SavePassword: qCDebug(POP3RESOURCE_LOG) << "================ Starting state SavePassword ==================="; if (mSavePassword) { qCDebug(POP3RESOURCE_LOG) << "Writing password back to the wallet."; Q_EMIT status(Running, i18n("Saving password to the wallet.")); mWallet = Wallet::openWallet(Wallet::NetworkWallet(), winIdForDialogs(), Wallet::Asynchronous); if (mWallet) { connect(mWallet, &KWallet::Wallet::walletOpened, this, &POP3Resource::walletOpenedForSaving); } else { finish(); } } else { finish(); } break; case CheckRemovingMessage: qCDebug(POP3RESOURCE_LOG) << "================ Starting state CheckRemovingMessage ==================="; checkRemovingMessageFromServer(); break; } } void POP3Resource::checkRemovingMessageFromServer() { const QList idToDeleteMessage = shouldDeleteId(-1); if (!idToDeleteMessage.isEmpty()) { mIdsWaitingToDelete << idToDeleteMessage; if (!mDeleteJob) { mDeleteJob = new DeleteJob(mPopSession); mDeleteJob->setDeleteIds(mIdsWaitingToDelete); mIdsWaitingToDelete.clear(); connect(mDeleteJob, &DeleteJob::result, this, &POP3Resource::deleteJobResult); mDeleteJob->start(); } } else { advanceState(Save); } } void POP3Resource::localFolderRequestJobFinished(KJob *job) { if (job->error()) { cancelSync(i18n("Error while trying to get the local inbox folder, " "aborting mail check.") + QLatin1Char('\n') + job->errorString()); return; } if (mTestLocalInbox) { KMessageBox::information(nullptr, i18n("The folder you deleted was associated with the account " "%1 which delivered mail into it. The folder the account " "delivers new mail into was reset to the main Inbox folder.", name())); } mTestLocalInbox = false; mTargetCollection = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Inbox); Q_ASSERT(mTargetCollection.isValid()); advanceState(Precommand); } void POP3Resource::targetCollectionFetchJobFinished(KJob *job) { if (job->error()) { if (!mTestLocalInbox) { mTestLocalInbox = true; - Settings::self()->setTargetCollection(-1); + mSettings.setTargetCollection(-1); advanceState(FetchTargetCollection); return; } else { cancelSync(i18n("Error while trying to get the folder for incoming mail, " "aborting mail check.") + QLatin1Char('\n') + job->errorString()); mTestLocalInbox = false; return; } } mTestLocalInbox = false; Akonadi::CollectionFetchJob *fetchJob = qobject_cast(job); Q_ASSERT(fetchJob); Q_ASSERT(fetchJob->collections().size() <= 1); if (fetchJob->collections().isEmpty()) { cancelSync(i18n("Could not find folder for incoming mail, aborting mail check.")); return; } else { mTargetCollection = fetchJob->collections().at(0); advanceState(Precommand); } } void POP3Resource::precommandResult(KJob *job) { if (job->error()) { cancelSync(i18n("Error while executing precommand.") +QLatin1Char('\n') + job->errorString()); return; } else { advanceState(RequestPassword); } } void POP3Resource::loginJobResult(KJob *job) { if (job->error()) { qCDebug(POP3RESOURCE_LOG) << job->error() << job->errorText(); if (job->error() == KIO::ERR_CANNOT_LOGIN) { mAskAgain = true; } - cancelSync(i18n("Unable to login to the server %1.", Settings::self()->host()) + cancelSync(i18n("Unable to login to the server %1.", mSettings.host()) +QLatin1Char('\n') + job->errorString()); } else { advanceState(List); } } void POP3Resource::listJobResult(KJob *job) { if (job->error()) { cancelSync(i18n("Error while getting the list of messages on the server.") +QLatin1Char('\n') + job->errorString()); } else { ListJob *listJob = qobject_cast(job); Q_ASSERT(listJob); mIdsToSizeMap = listJob->idList(); mIdsToSaveValid = false; qCDebug(POP3RESOURCE_LOG) << "IdsToSizeMap size" << mIdsToSizeMap.size(); advanceState(UIDList); } } void POP3Resource::uidListJobResult(KJob *job) { if (job->error()) { cancelSync(i18n("Error while getting list of unique mail identifiers from the server.") +QLatin1Char('\n') + job->errorString()); } else { UIDListJob *listJob = qobject_cast(job); Q_ASSERT(listJob); mIdsToUidsMap = listJob->uidList(); mUidsToIdsMap = listJob->idList(); qCDebug(POP3RESOURCE_LOG) << "IdsToUidsMap size" << mIdsToUidsMap.size(); qCDebug(POP3RESOURCE_LOG) << "UidsToIdsMap size" << mUidsToIdsMap.size(); mUidListValid = !mIdsToUidsMap.isEmpty() || mIdsToSizeMap.isEmpty(); - if (Settings::self()->leaveOnServer() && !mUidListValid) { + if (mSettings.leaveOnServer() && !mUidListValid) { // FIXME: this needs a proper parent window KMessageBox::sorry(nullptr, i18n("Your POP3 server (Account: %1) does not support " "the UIDL command: this command is required to determine, in a reliable way, " "which of the mails on the server KMail has already seen before;\n" "the feature to leave the mails on the server will therefore not " "work properly.", name())); } advanceState(Download); } } void POP3Resource::fetchJobResult(KJob *job) { if (job->error()) { cancelSync(i18n("Error while fetching mails from the server.") +QLatin1Char('\n') + job->errorString()); return; } else { qCDebug(POP3RESOURCE_LOG) << "Downloaded" << mDownloadedIDs.size() << "mails"; if (!mIdsToDownload.isEmpty()) { qCWarning(POP3RESOURCE_LOG) << "We did not download all messages, there are still some remaining " "IDs, even though we requested their download:" << mIdsToDownload; } advanceState(Save); } } void POP3Resource::messageFinished(int messageId, KMime::Message::Ptr message) { if (mState != Download) { // This can happen if the slave does not get notified in time about the fact // that the job was killed return; } //qCDebug(POP3RESOURCE_LOG) << "Got message" << messageId // << "with subject" << message->subject()->asUnicodeString(); Akonadi::Item item; item.setMimeType(QStringLiteral("message/rfc822")); item.setPayload(message); Akonadi::Pop3ResourceAttribute *attr = item.attribute(Akonadi::Item::AddIfMissing); attr->setPop3AccountName(identifier()); Akonadi::MessageFlags::copyMessageFlags(*message, item); ItemCreateJob *itemCreateJob = new ItemCreateJob(item, mTargetCollection); mPendingCreateJobs.insert(itemCreateJob, messageId); connect(itemCreateJob, &ItemCreateJob::result, this, &POP3Resource::itemCreateJobResult); mDownloadedIDs.append(messageId); mIdsToDownload.removeAll(messageId); } void POP3Resource::messageDownloadProgress(KJob *job, KJob::Unit unit, qulonglong totalBytes) { Q_UNUSED(totalBytes); Q_UNUSED(unit); Q_ASSERT(unit == KJob::Bytes); QString statusMessage; const int totalMessages = mIdsToDownload.size() + mDownloadedIDs.size(); int bytesRemainingOnServer = 0; - foreach (const QString &alreadyDownloadedUID, Settings::self()->seenUidList()) { + foreach (const QString &alreadyDownloadedUID, mSettings.seenUidList()) { const int alreadyDownloadedID = mUidsToIdsMap.value(alreadyDownloadedUID, -1); if (alreadyDownloadedID != -1) { bytesRemainingOnServer += mIdsToSizeMap.value(alreadyDownloadedID); } } - if (Settings::self()->leaveOnServer() && bytesRemainingOnServer > 0) { + if (mSettings.leaveOnServer() && bytesRemainingOnServer > 0) { statusMessage = i18n("Fetching message %1 of %2 (%3 of %4 KB) for %5 " "(%6 KB remain on the server).", mDownloadedIDs.size() + 1, totalMessages, job->processedAmount(KJob::Bytes) / 1024, job->totalAmount(KJob::Bytes) / 1024, name(), bytesRemainingOnServer / 1024); } else { statusMessage = i18n("Fetching message %1 of %2 (%3 of %4 KB) for %5", mDownloadedIDs.size() + 1, totalMessages, job->processedAmount(KJob::Bytes) / 1024, job->totalAmount(KJob::Bytes) / 1024, name()); } Q_EMIT status(Running, statusMessage); Q_EMIT percent(job->percent()); } void POP3Resource::itemCreateJobResult(KJob *job) { if (mState != Download && mState != Save) { // This can happen if the slave does not get notified in time about the fact // that the job was killed return; } ItemCreateJob *createJob = qobject_cast(job); Q_ASSERT(createJob); if (job->error()) { cancelSync(i18n("Unable to store downloaded mails.") +QLatin1Char('\n') + job->errorString()); return; } const int idOfMessageJustCreated = mPendingCreateJobs.value(createJob, -1); Q_ASSERT(idOfMessageJustCreated != -1); mPendingCreateJobs.remove(createJob); mIDsStored.append(idOfMessageJustCreated); //qCDebug(POP3RESOURCE_LOG) << "Just stored message with ID" << idOfMessageJustCreated // << "on the Akonadi server"; const QList idToDeleteMessage = shouldDeleteId(idOfMessageJustCreated); if (!idToDeleteMessage.isEmpty()) { mIdsWaitingToDelete << idToDeleteMessage; if (!mDeleteJob) { mDeleteJob = new DeleteJob(mPopSession); mDeleteJob->setDeleteIds(mIdsWaitingToDelete); mIdsWaitingToDelete.clear(); connect(mDeleteJob, &DeleteJob::result, this, &POP3Resource::deleteJobResult); mDeleteJob->start(); } } // Have all create jobs finished? Go to the next state, then if (shouldAdvanceToQuitState()) { advanceState(Quit); } } int POP3Resource::idToTime(int id) const { const QString uid = mIdsToUidsMap.value(id); if (!uid.isEmpty()) { - const QList seenUIDs = Settings::self()->seenUidList(); - const QList timeOfSeenUids = Settings::self()->seenUidTimeList(); + const QList seenUIDs = mSettings.seenUidList(); + const QList timeOfSeenUids = mSettings.seenUidTimeList(); Q_ASSERT(seenUIDs.size() == timeOfSeenUids.size()); const int index = seenUIDs.indexOf(uid); if (index != -1 && (index < timeOfSeenUids.size())) { return timeOfSeenUids.at(index); } } // If we don't find any mail, either we have no UID, or it is not in the seen UID // list. In that case, we assume that the mail is new, i.e. from now return time(nullptr); } int POP3Resource::idOfOldestMessage(const QSet &idList) const { int timeOfOldestMessage = time(nullptr) + 999; int idOfOldestMessage = -1; foreach (int id, idList) { const int idTime = idToTime(id); if (idTime < timeOfOldestMessage) { timeOfOldestMessage = idTime; idOfOldestMessage = id; } } Q_ASSERT(idList.isEmpty() || idOfOldestMessage != -1); return idOfOldestMessage; } QList POP3Resource::shouldDeleteId(int downloadedId) const { QList idsToDeleteFromServer; // By default, we delete all messages. But if we have "leave on server" // rules, we can save some messages. - if (Settings::self()->leaveOnServer()) { + if (mSettings.leaveOnServer()) { idsToDeleteFromServer = mIdsToSizeMap.keys(); if (!mIdsToSaveValid) { mIdsToSaveValid = true; mIdsToSave.clear(); #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) const QSet idsOnServer = QSet::fromList(idsToDeleteFromServer); #else const QSet idsOnServer(idsToDeleteFromServer.constBegin(), idsToDeleteFromServer.constEnd()); #endif // If the time-limited leave rule is checked, add the newer messages to // the list of messages to keep - if (Settings::self()->leaveOnServerDays() > 0) { + if (mSettings.leaveOnServerDays() > 0) { const int secondsPerDay = 86400; - time_t timeLimit = time(nullptr) - (secondsPerDay * Settings::self()->leaveOnServerDays()); + time_t timeLimit = time(nullptr) - (secondsPerDay * mSettings.leaveOnServerDays()); foreach (int idToDelete, idsOnServer) { const int msgTime = idToTime(idToDelete); if (msgTime >= timeLimit) { mIdsToSave << idToDelete; } else { qCDebug(POP3RESOURCE_LOG) << "Message" << idToDelete << "is too old and will be deleted."; } } } // Otherwise, add all messages to the list of messages to keep - this may // be reduced in the following number-limited leave rule and size-limited // leave rule checks else { mIdsToSave = idsOnServer; } // // Delete more old messages if there are more than mLeaveOnServerCount // - if (Settings::self()->leaveOnServerCount() > 0) { - const int numToDelete = mIdsToSave.count() - Settings::self()->leaveOnServerCount(); + if (mSettings.leaveOnServerCount() > 0) { + const int numToDelete = mIdsToSave.count() - mSettings.leaveOnServerCount(); if (numToDelete > 0 && numToDelete < mIdsToSave.count()) { // Get rid of the first numToDelete messages for (int i = 0; i < numToDelete; i++) { mIdsToSave.remove(idOfOldestMessage(mIdsToSave)); } } else if (numToDelete >= mIdsToSave.count()) { mIdsToSave.clear(); } } // // Delete more old messages until we're under mLeaveOnServerSize MBs // - if (Settings::self()->leaveOnServerSize() > 0) { - const qint64 limitInBytes = Settings::self()->leaveOnServerSize() * (1024 * 1024); + if (mSettings.leaveOnServerSize() > 0) { + const qint64 limitInBytes = mSettings.leaveOnServerSize() * (1024 * 1024); qint64 sizeOnServerAfterDeletion = 0; for (int id : qAsConst(mIdsToSave)) { sizeOnServerAfterDeletion += mIdsToSizeMap.value(id); } while (sizeOnServerAfterDeletion > limitInBytes) { int oldestId = idOfOldestMessage(mIdsToSave); mIdsToSave.remove(oldestId); sizeOnServerAfterDeletion -= mIdsToSizeMap.value(oldestId); } } } // Now save the messages from deletion // foreach (int idToSave, mIdsToSave) { idsToDeleteFromServer.removeAll(idToSave); } if (downloadedId != -1 && !mIdsToSave.contains(downloadedId)) { idsToDeleteFromServer << downloadedId; } } else { if (downloadedId != -1) { idsToDeleteFromServer << downloadedId; } else { idsToDeleteFromServer << mIdsToSizeMap.keys(); } } return idsToDeleteFromServer; } void POP3Resource::deleteJobResult(KJob *job) { if (job->error()) { cancelSync(i18n("Failed to delete the messages from the server.") +QLatin1Char('\n') + job->errorString()); return; } DeleteJob *finishedDeleteJob = qobject_cast(job); Q_ASSERT(finishedDeleteJob); Q_ASSERT(finishedDeleteJob == mDeleteJob); mDeletedIDs = finishedDeleteJob->deletedIDs(); // Remove all deleted messages from the list of already downloaded messages, // as it is no longer necessary to store them (they just waste space) - QList seenUIDs = Settings::self()->seenUidList(); - QList timeOfSeenUids = Settings::self()->seenUidTimeList(); + QList seenUIDs = mSettings.seenUidList(); + QList timeOfSeenUids = mSettings.seenUidTimeList(); Q_ASSERT(seenUIDs.size() == timeOfSeenUids.size()); foreach (int deletedId, mDeletedIDs) { const QString deletedUID = mIdsToUidsMap.value(deletedId); if (!deletedUID.isEmpty()) { int index = seenUIDs.indexOf(deletedUID); if (index != -1) { // TEST qCDebug(POP3RESOURCE_LOG) << "Removing UID" << deletedUID << "from the seen UID list, as it was deleted."; seenUIDs.removeAt(index); timeOfSeenUids.removeAt(index); } } mIdsToUidsMap.remove(deletedId); mIdsToSizeMap.remove(deletedId); } - Settings::self()->setSeenUidList(seenUIDs); - Settings::self()->setSeenUidTimeList(timeOfSeenUids); - Settings::self()->save(); + mSettings.setSeenUidList(seenUIDs); + mSettings.setSeenUidTimeList(timeOfSeenUids); + mSettings.save(); mDeleteJob = nullptr; if (!mIdsWaitingToDelete.isEmpty()) { mDeleteJob = new DeleteJob(mPopSession); mDeleteJob->setDeleteIds(mIdsWaitingToDelete); mIdsWaitingToDelete.clear(); connect(mDeleteJob, &DeleteJob::result, this, &POP3Resource::deleteJobResult); mDeleteJob->start(); } if (shouldAdvanceToQuitState()) { advanceState(Quit); } else if (mDeleteJob == nullptr) { advanceState(Save); } } void POP3Resource::finish() { qCDebug(POP3RESOURCE_LOG) << "================= Mail check finished. ============================="; saveSeenUIDList(); if (!mIntervalCheckInProgress) { collectionsRetrieved(Akonadi::Collection::List()); } if (mDownloadedIDs.isEmpty()) { Q_EMIT status(Idle, i18n("Finished mail check, no message downloaded.")); } else { Q_EMIT status(Idle, i18np("Finished mail check, 1 message downloaded.", "Finished mail check, %1 messages downloaded.", mDownloadedIDs.size())); } resetState(); } bool POP3Resource::shouldAdvanceToQuitState() const { return mState == Save && mPendingCreateJobs.isEmpty() && mIdsWaitingToDelete.isEmpty() && !mDeleteJob; } void POP3Resource::quitJobResult(KJob *job) { if (job->error()) { cancelSync(i18n("Unable to complete the mail fetch.") +QLatin1Char('\n') + job->errorString()); return; } advanceState(SavePassword); } void POP3Resource::slotSessionError(int errorCode, const QString &errorMessage) { qCWarning(POP3RESOURCE_LOG) << "Error in our session, unrelated to a currently running job!"; cancelSync(KIO::buildErrorString(errorCode, errorMessage)); } void POP3Resource::saveSeenUIDList() { - QList seenUIDs = Settings::self()->seenUidList(); - QList timeOfSeenUIDs = Settings::self()->seenUidTimeList(); + QList seenUIDs = mSettings.seenUidList(); + QList timeOfSeenUIDs = mSettings.seenUidTimeList(); // // Find the messages that we have successfully stored, but did not actually get // deleted. // Those messages, we have to remember, so we don't download them again. // QList idsOfMessagesDownloadedButNotDeleted = mIDsStored; foreach (int deletedId, mDeletedIDs) { idsOfMessagesDownloadedButNotDeleted.removeAll(deletedId); } QList uidsOfMessagesDownloadedButNotDeleted; foreach (int id, idsOfMessagesDownloadedButNotDeleted) { const QString uid = mIdsToUidsMap.value(id); if (!uid.isEmpty()) { uidsOfMessagesDownloadedButNotDeleted.append(uid); } } Q_ASSERT(seenUIDs.size() == timeOfSeenUIDs.size()); foreach (const QString &uid, uidsOfMessagesDownloadedButNotDeleted) { if (!seenUIDs.contains(uid)) { seenUIDs.append(uid); timeOfSeenUIDs.append(time(nullptr)); } } // // If we have a valid UID list from the server, delete those UIDs that are in // the seenUidList but are not on the server. // This can happen if someone else than this resource deleted the mails from the // server which we kept here. // if (mUidListValid) { QList::iterator uidIt = seenUIDs.begin(); QList::iterator timeIt = timeOfSeenUIDs.begin(); while (uidIt != seenUIDs.end()) { const QString curSeenUID = *uidIt; if (!mUidsToIdsMap.contains(curSeenUID)) { // Ok, we have a UID in the seen UID list that is not anymore on the server. // Therefore remove it from the seen UID list, it is not needed there anymore, // it just wastes space. uidIt = seenUIDs.erase(uidIt); timeIt = timeOfSeenUIDs.erase(timeIt); } else { ++uidIt; ++timeIt; } } } else { qCWarning(POP3RESOURCE_LOG) << "UID list from server is not valid."; } // // Now save it in the settings // qCDebug(POP3RESOURCE_LOG) << "The seen UID list has" << seenUIDs.size() << "entries"; - Settings::self()->setSeenUidList(seenUIDs); - Settings::self()->setSeenUidTimeList(timeOfSeenUIDs); - Settings::self()->save(); + mSettings.setSeenUidList(seenUIDs); + mSettings.setSeenUidTimeList(timeOfSeenUIDs); + mSettings.save(); } void POP3Resource::cancelSync(const QString &errorMessage, bool error) { if (error) { cancelTask(errorMessage); qCWarning(POP3RESOURCE_LOG) << "============== ERROR DURING POP3 SYNC =========================="; qCWarning(POP3RESOURCE_LOG) << errorMessage; KNotification::event(QStringLiteral("mail-check-error"), name(), errorMessage.toHtmlEscaped(), QString(), nullptr, KNotification::CloseOnTimeout, QStringLiteral("akonadi_pop3_resource")); } else { qCDebug(POP3RESOURCE_LOG) << "Canceled the sync, but no error."; cancelTask(); } saveSeenUIDList(); resetState(); } void POP3Resource::resetState() { mState = Idle; mTargetCollection = Collection(-1); mIdsToSizeMap.clear(); mIdsToUidsMap.clear(); mUidsToIdsMap.clear(); mDownloadedIDs.clear(); mIdsToDownload.clear(); mPendingCreateJobs.clear(); mIDsStored.clear(); mDeletedIDs.clear(); mIdsWaitingToDelete.clear(); if (mDeleteJob) { mDeleteJob->deleteLater(); mDeleteJob = nullptr; } mUidListValid = false; mIntervalCheckInProgress = false; mSavePassword = false; updateIntervalTimer(); delete mWallet; mWallet = nullptr; if (mPopSession) { // Closing the POP session means the KIO slave will get disconnected, which // automatically issues the QUIT command. // Delete the POP session later, otherwise the scheduler makes us crash mPopSession->abortCurrentJob(); mPopSession->deleteLater(); mPopSession = nullptr; } } void POP3Resource::startMailCheck() { resetState(); mIntervalTimer->stop(); Q_EMIT percent(0); // Otherwise the value from the last sync is taken advanceState(FetchTargetCollection); } void POP3Resource::retrieveCollections() { if (mState == Idle) { startMailCheck(); } else { cancelSync( i18n("Mail check already in progress, unable to start a second check.")); } } void POP3Resource::clearCachedPassword() { mPassword.clear(); } void POP3Resource::cleanup() { if (mWallet && mWallet->isOpen() && mWallet->hasFolder(QStringLiteral("pop3"))) { mWallet->setFolder(QStringLiteral("pop3")); if (mWallet->hasEntry(identifier())) { mWallet->removeEntry(identifier()); } } ResourceBase::cleanup(); } void POP3Resource::doSetOnline(bool online) { ResourceBase::doSetOnline(online); if (online) { Q_EMIT status(Idle, i18n("Ready")); } else { if (mState != Idle) { cancelSync(i18n("Mail check aborted after going offline."), false /* no error */); } Q_EMIT status(Idle, i18n("Offline")); delete mWallet; mWallet = nullptr; clearCachedPassword(); } } AKONADI_RESOURCE_MAIN(POP3Resource) diff --git a/resources/pop3/pop3resource.h b/resources/pop3/pop3resource.h index 0c445dd6a..c00469666 100644 --- a/resources/pop3/pop3resource.h +++ b/resources/pop3/pop3resource.h @@ -1,202 +1,206 @@ /* Copyright 2009 Thomas McGuire 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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. */ #ifndef POP3RESOURCE_H #define POP3RESOURCE_H #include #include #include #include +#include "settings.h" + class DeleteJob; namespace Akonadi { class ItemCreateJob; } namespace KWallet { class Wallet; } class POPSession; class QTimer; class POP3Resource : public Akonadi::ResourceBase, public Akonadi::AgentBase::Observer { Q_OBJECT public: explicit POP3Resource(const QString &id); ~POP3Resource() override; void clearCachedPassword(); void cleanup() override; protected: using ResourceBase::retrieveItems; // Suppress -Woverload-virtual protected Q_SLOTS: void retrieveCollections() override; void retrieveItems(const Akonadi::Collection &col) override; bool retrieveItem(const Akonadi::Item &item, const QSet &parts) override; protected: void aboutToQuit() override; void doSetOnline(bool online) override; private Q_SLOTS: void slotAbortRequested(); void intervalCheckTriggered(); void configurationChanged(); // Error unrelated to a state void slotSessionError(int errorCode, const QString &errorMessage); // For state FetchTargetCollection void targetCollectionFetchJobFinished(KJob *job); void localFolderRequestJobFinished(KJob *job); // For state Precommand void precommandResult(KJob *job); // For state RequestPassword void walletOpenedForLoading(bool success); // For state Login void loginJobResult(KJob *job); // For state List void listJobResult(KJob *job); // For state UIDList void uidListJobResult(KJob *job); // For state Download void messageFinished(int messageId, KMime::Message::Ptr message); void fetchJobResult(KJob *job); void messageDownloadProgress(KJob *job, KJob::Unit unit, qulonglong totalBytes); // For state Save void itemCreateJobResult(KJob *job); // For state Delete void deleteJobResult(KJob *job); // For state Quit void quitJobResult(KJob *job); // For state SavePassword void walletOpenedForSaving(bool success); private: enum State { Idle, FetchTargetCollection, Precommand, RequestPassword, Connect, Login, List, UIDList, Download, Save, Quit, SavePassword, CheckRemovingMessage }; void resetState(); void doStateStep(); void advanceState(State nextState); void cancelSync(const QString &errorMessage, bool error = true); void saveSeenUIDList(); QList shouldDeleteId(int downloadedId) const; int idToTime(int id) const; int idOfOldestMessage(const QSet &idList) const; void startMailCheck(); void updateIntervalTimer(); void showPasswordDialog(const QString &queryText); QString buildLabelForPasswordDialog(const QString &detailedError) const; void checkRemovingMessageFromServer(); void finish(); bool shouldAdvanceToQuitState() const; State mState; Akonadi::Collection mTargetCollection; POPSession *mPopSession = nullptr; bool mAskAgain = false; QTimer *mIntervalTimer = nullptr; bool mIntervalCheckInProgress = false; QString mPassword; bool mSavePassword = false; bool mTestLocalInbox = false; KWallet::Wallet *mWallet = nullptr; // Maps IDs on the server to message sizes on the server QMap mIdsToSizeMap; // Maps IDs on the server to UIDs on the server. // This can be empty, if the server doesn't support UIDL QMap mIdsToUidsMap; // Maps UIDs on the server to IDs on the server. // This can be empty, if the server doesn't support UIDL QMap mUidsToIdsMap; // Whether we actually received a valid UID list from the server bool mUidListValid; // IDs of messages that we have successfully downloaded. This does _not_ mean // that the messages corresponding to the IDs are stored in Akonadi yet QList mDownloadedIDs; // IDs of messages that we want to download and that we have started the // FetchJob with. After the FetchJob, this should be empty, except if there // was some error QList mIdsToDownload; // After downloading a message, we store it in Akonadi by using an ItemCreateJob. // This map stores the currently running ItemCreateJob's and their corresponding // POP3 IDs. // When an ItemCreateJob finished, it is removed from this map. // The Save state waits until this map becomes empty. QMap mPendingCreateJobs; // List of message IDs that were successfully stored in Akonadi QList mIDsStored; // List of message IDs that were successfully deleted QList mDeletedIDs; // List of message IDs that we want to delete with the next delete job QList mIdsWaitingToDelete; // List of message IDs that we want to keep on the server mutable QSet mIdsToSave; mutable bool mIdsToSaveValid; // Current deletion job in process DeleteJob *mDeleteJob = nullptr; + + Settings mSettings; }; #endif diff --git a/resources/pop3/settings.cpp b/resources/pop3/settings.cpp index 1f91ef30e..367dfa311 100644 --- a/resources/pop3/settings.cpp +++ b/resources/pop3/settings.cpp @@ -1,85 +1,60 @@ /* Copyright 2010 Thomas McGuire 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "settings.h" #include "settingsadaptor.h" #include #include "pop3resource_debug.h" -class SettingsHelper -{ -public: - SettingsHelper() : q(nullptr) - { - } - - ~SettingsHelper() - { - qCWarning(POP3RESOURCE_LOG) << q; - delete q; - q = nullptr; - } - - Settings *q; -}; - -Q_GLOBAL_STATIC(SettingsHelper, s_globalSettings) - -Settings *Settings::self() -{ - Q_ASSERT_X(s_globalSettings->q, "Settings::self()", "You must create an instance first!"); - return s_globalSettings->q; -} - -Settings::Settings(const KSharedConfigPtr &config) +Settings::Settings(const KSharedConfigPtr &config, Options options) : SettingsBase(config) { - Q_ASSERT(!s_globalSettings->q); - s_globalSettings->q = this; - new SettingsAdaptor(this); - QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), this, - QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents); + if (options & Option::ExportToDBus) { + new SettingsAdaptor(this); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), this, + QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents); + } } void Settings::setWindowId(WId id) { mWinId = id; } void Settings::setResourceId(const QString &resourceIdentifier) { mResourceId = resourceIdentifier; } void Settings::setPassword(const QString &password) { using namespace KWallet; Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), mWinId, Wallet::Synchronous); if (wallet && wallet->isOpen()) { if (!wallet->hasFolder(QStringLiteral("pop3"))) { wallet->createFolder(QStringLiteral("pop3")); } wallet->setFolder(QStringLiteral("pop3")); wallet->writePassword(mResourceId, password); } else { qCWarning(POP3RESOURCE_LOG) << "Unable to open wallet!"; } delete wallet; } diff --git a/resources/pop3/settings.h b/resources/pop3/settings.h index c8319cf1e..9fac82657 100644 --- a/resources/pop3/settings.h +++ b/resources/pop3/settings.h @@ -1,48 +1,56 @@ /* Copyright 2010 Thomas McGuire 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 ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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. */ #ifndef SETTINGS_H #define SETTINGS_H #include "settingsbase.h" #include +#include /** * Extended settings class that allows setting the password over dbus, which is used by the * wizard. */ class Settings : public SettingsBase { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.Akonadi.POP3.Wallet") public: - Settings(const KSharedConfigPtr &config); + enum class Option { + NoOption = 0, + ExportToDBus = 1 + }; + Q_DECLARE_FLAGS(Options, Option) + + explicit Settings(const KSharedConfigPtr &config, Options options = Option::ExportToDBus); void setWindowId(WId id); void setResourceId(const QString &resourceIdentifier); - static Settings *self(); public Q_SLOTS: Q_SCRIPTABLE void setPassword(const QString &password); private: WId mWinId; QString mResourceId; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(Settings::Options) + #endif