diff --git a/resources/dav/resource/settings.cpp b/resources/dav/resource/settings.cpp index e289403f9..d46c6ec7e 100644 --- a/resources/dav/resource/settings.cpp +++ b/resources/dav/resource/settings.cpp @@ -1,568 +1,568 @@ /* Copyright (c) 2009 Grégory Oestreicher Based on an original work for the IMAP resource which is : Copyright (c) 2008 Volker Krause Copyright (c) 2008 Omat Holding B.V. 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. */ #include "settings.h" #include "settingsadaptor.h" #include "utils.h" #include "davresource_debug.h" #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class SettingsHelper { public: SettingsHelper() : q(nullptr) { } ~SettingsHelper() { delete q; } Settings *q; }; Q_GLOBAL_STATIC(SettingsHelper, s_globalSettings) Settings::UrlConfiguration::UrlConfiguration() { } Settings::UrlConfiguration::UrlConfiguration(const QString &serialized) { const QStringList splitString = serialized.split(QLatin1Char('|')); if (splitString.size() == 3) { mUrl = splitString.at(2); mProtocol = KDAV::ProtocolInfo::protocolByName(splitString.at(1)); mUser = splitString.at(0); } } QString Settings::UrlConfiguration::serialize() { QString serialized = mUser; serialized.append(QLatin1Char('|')).append(KDAV::ProtocolInfo::protocolName(KDAV::Protocol(mProtocol))); serialized.append(QLatin1Char('|')).append(mUrl); return serialized; } Settings *Settings::self() { if (!s_globalSettings->q) { new Settings; s_globalSettings->q->load(); } return s_globalSettings->q; } Settings::Settings() : SettingsBase() , mWinId(0) { Q_ASSERT(!s_globalSettings->q); s_globalSettings->q = this; new SettingsAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), this, QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents); if (settingsVersion() == 1) { updateToV2(); } else if (settingsVersion() == 2) { updateToV3(); } } Settings::~Settings() { QMapIterator it(mUrls); while (it.hasNext()) { it.next(); delete it.value(); } } void Settings::setWinId(WId winId) { mWinId = winId; } void Settings::cleanup() { KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), mWinId); if (wallet && wallet->isOpen()) { if (wallet->hasFolder(KWallet::Wallet::PasswordFolder())) { wallet->setFolder(KWallet::Wallet::PasswordFolder()); const QString entry = mResourceIdentifier + QLatin1Char(',') + QStringLiteral("$default$"); wallet->removeEntry(entry); } delete wallet; } QFile cacheFile(mCollectionsUrlsMappingCache); cacheFile.remove(); } void Settings::setResourceIdentifier(const QString &identifier) { mResourceIdentifier = identifier; } void Settings::setDefaultPassword(const QString &password) { savePassword(mResourceIdentifier, QStringLiteral("$default$"), password); } QString Settings::defaultPassword() { return loadPassword(mResourceIdentifier, QStringLiteral("$default$")); } KDAV::DavUrl::List Settings::configuredDavUrls() { if (mUrls.isEmpty()) { buildUrlsList(); } KDAV::DavUrl::List davUrls; davUrls.reserve(mUrls.count()); QMap::const_iterator it = mUrls.cbegin(); const QMap::const_iterator itEnd = mUrls.cend(); for (; it != itEnd; ++it) { QStringList split = it.key().split(QLatin1Char(',')); davUrls << configuredDavUrl(KDAV::ProtocolInfo::protocolByName(split.at(1)), split.at(0)); } return davUrls; } KDAV::DavUrl Settings::configuredDavUrl(KDAV::Protocol proto, const QString &searchUrl, const QString &finalUrl) { if (mUrls.isEmpty()) { buildUrlsList(); } QUrl fullUrl; if (!finalUrl.isEmpty()) { fullUrl = QUrl::fromUserInput(finalUrl); if (finalUrl.startsWith(QLatin1Char('/'))) { QUrl searchQUrl(searchUrl); fullUrl.setHost(searchQUrl.host()); fullUrl.setScheme(searchQUrl.scheme()); fullUrl.setPort(searchQUrl.port()); } } else { fullUrl = QUrl::fromUserInput(searchUrl); } const QString user = username(proto, searchUrl); fullUrl.setUserName(user); fullUrl.setPassword(password(proto, searchUrl)); return KDAV::DavUrl(fullUrl, proto); } KDAV::DavUrl Settings::davUrlFromCollectionUrl(const QString &collectionUrl, const QString &finalUrl) { if (mCollectionsUrlsMapping.isEmpty()) { loadMappings(); } KDAV::DavUrl davUrl; QString targetUrl = finalUrl.isEmpty() ? collectionUrl : finalUrl; if (mCollectionsUrlsMapping.contains(collectionUrl)) { QStringList split = mCollectionsUrlsMapping[ collectionUrl ].split(QLatin1Char(',')); if (split.size() == 2) { davUrl = configuredDavUrl(KDAV::ProtocolInfo::protocolByName(split.at(1)), split.at(0), targetUrl); } } return davUrl; } void Settings::addCollectionUrlMapping(KDAV::Protocol proto, const QString &collectionUrl, const QString &configuredUrl) { if (mCollectionsUrlsMapping.isEmpty()) { loadMappings(); } QString value = configuredUrl + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(proto); mCollectionsUrlsMapping.insert(collectionUrl, value); // Update the cache now //QMap tmp( mCollectionsUrlsMapping ); QFileInfo cacheFileInfo = QFileInfo(mCollectionsUrlsMappingCache); if (!cacheFileInfo.dir().exists()) { QDir::root().mkpath(cacheFileInfo.dir().absolutePath()); } QFile cacheFile(mCollectionsUrlsMappingCache); if (cacheFile.open(QIODevice::WriteOnly)) { QDataStream cache(&cacheFile); cache.setVersion(QDataStream::Qt_4_7); cache << mCollectionsUrlsMapping; cacheFile.close(); } } QStringList Settings::mappedCollections(KDAV::Protocol proto, const QString &configuredUrl) { if (mCollectionsUrlsMapping.isEmpty()) { loadMappings(); } QString value = configuredUrl + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(proto); return mCollectionsUrlsMapping.keys(value); } void Settings::reloadConfig() { buildUrlsList(); updateRemoteUrls(); loadMappings(); } void Settings::newUrlConfiguration(Settings::UrlConfiguration *urlConfig) { QString key = urlConfig->mUrl + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(KDAV::Protocol(urlConfig->mProtocol)); if (mUrls.contains(key)) { removeUrlConfiguration(KDAV::Protocol(urlConfig->mProtocol), urlConfig->mUrl); } mUrls[ key ] = urlConfig; if (urlConfig->mUser != QLatin1String("$default$")) { savePassword(key, urlConfig->mUser, urlConfig->mPassword); } updateRemoteUrls(); } void Settings::removeUrlConfiguration(KDAV::Protocol proto, const QString &url) { QString key = url + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(proto); if (!mUrls.contains(key)) { return; } delete mUrls[ key ]; mUrls.remove(key); updateRemoteUrls(); } Settings::UrlConfiguration *Settings::urlConfiguration(KDAV::Protocol proto, const QString &url) { QString key = url + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(proto); UrlConfiguration *ret = nullptr; if (mUrls.contains(key)) { ret = mUrls[ key ]; } return ret; } // KDAV::Protocol Settings::protocol( const QString &url ) const // { // if ( mUrls.contains( url ) ) // return KDAV::Protocol( mUrls[ url ]->mProtocol ); // else // returnKDAV::CalDav; // } QString Settings::username(KDAV::Protocol proto, const QString &url) const { QString key = url + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(proto); if (mUrls.contains(key)) { if (mUrls[ key ]->mUser == QLatin1String("$default$")) { return defaultUsername(); } else { return mUrls[ key ]->mUser; } } else { return QString(); } } QString Settings::password(KDAV::Protocol proto, const QString &url) { QString key = url + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(proto); if (mUrls.contains(key)) { if (mUrls[ key ]->mUser == QLatin1String("$default$")) { return defaultPassword(); } else { return mUrls[ key ]->mPassword; } } else { return QString(); } } QDateTime Settings::getSyncRangeStart() const { QDateTime start = QDateTime::currentDateTimeUtc(); start.setTime(QTime()); const int delta = -syncRangeStartNumber().toUInt(); if (syncRangeStartType() == QLatin1Char('D')) { start = start.addDays(delta); } else if (syncRangeStartType() == QLatin1Char('M')) { start = start.addMonths(delta); } else if (syncRangeStartType() == QLatin1Char('Y')) { start = start.addYears(delta); } else { start = QDateTime(); } return start; } void Settings::buildUrlsList() { foreach (const QString &serializedUrl, remoteUrls()) { UrlConfiguration *urlConfig = new UrlConfiguration(serializedUrl); QString key = urlConfig->mUrl + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(KDAV::Protocol(urlConfig->mProtocol)); QString pass = loadPassword(key, urlConfig->mUser); if (!pass.isNull()) { urlConfig->mPassword = pass; mUrls[ key ] = urlConfig; } else { delete urlConfig; } } } void Settings::loadMappings() { QString collectionsMappingCacheBase = QStringLiteral("akonadi-davgroupware/%1_c2u.dat").arg(QCoreApplication::applicationName()); mCollectionsUrlsMappingCache = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + collectionsMappingCacheBase; QFile collectionsMappingsCache(mCollectionsUrlsMappingCache); if (collectionsMappingsCache.exists()) { if (collectionsMappingsCache.open(QIODevice::ReadOnly)) { QDataStream cache(&collectionsMappingsCache); cache >> mCollectionsUrlsMapping; collectionsMappingsCache.close(); } } else if (!collectionsUrlsMappings().isEmpty()) { QByteArray rawMappings = QByteArray::fromBase64(collectionsUrlsMappings().toLatin1()); QDataStream stream(&rawMappings, QIODevice::ReadOnly); stream >> mCollectionsUrlsMapping; setCollectionsUrlsMappings(QString()); } } void Settings::updateRemoteUrls() { QStringList newUrls; newUrls.reserve(mUrls.count()); QMapIterator it(mUrls); while (it.hasNext()) { it.next(); newUrls << it.value()->serialize(); } setRemoteUrls(newUrls); } void Settings::savePassword(const QString &key, const QString &user, const QString &password) { QString entry = key + QLatin1Char(',') + user; mPasswordsCache[entry] = password; KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), mWinId); if (!wallet) { return; } if (!wallet->hasFolder(KWallet::Wallet::PasswordFolder())) { wallet->createFolder(KWallet::Wallet::PasswordFolder()); } if (!wallet->setFolder(KWallet::Wallet::PasswordFolder())) { return; } wallet->writePassword(entry, password); } QString Settings::loadPassword(const QString &key, const QString &user) { QString entry; QString pass; if (user == QLatin1String("$default$")) { entry = mResourceIdentifier + QLatin1Char(',') + user; } else { entry = key + QLatin1Char(',') + user; } if (mPasswordsCache.contains(entry)) { return mPasswordsCache[entry]; } KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), mWinId); if (wallet) { if (!wallet->hasFolder(KWallet::Wallet::PasswordFolder())) { wallet->createFolder(KWallet::Wallet::PasswordFolder()); } if (wallet->setFolder(KWallet::Wallet::PasswordFolder())) { if (!wallet->hasEntry(entry)) { pass = promptForPassword(user); wallet->writePassword(entry, pass); } else { wallet->readPassword(entry, pass); } } } if (pass.isNull() && !KWallet::Wallet::isEnabled()) { pass = promptForPassword(user); } if (!pass.isNull()) { mPasswordsCache[entry] = pass; } return pass; } QString Settings::promptForPassword(const QString &user) { QPointer dlg = new QDialog(); QString password; QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dlg); QVBoxLayout *mainLayout = new QVBoxLayout(dlg); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, dlg.data(), &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, dlg.data(), &QDialog::reject); QWidget *mainWidget = new QWidget(dlg); mainLayout->addWidget(mainWidget); mainLayout->addWidget(buttonBox); QVBoxLayout *vLayout = new QVBoxLayout(); mainWidget->setLayout(vLayout); QLabel *label = new QLabel(i18n("A password is required for user %1", (user == QLatin1String("$default$") ? defaultUsername() : user)), mainWidget ); vLayout->addWidget(label); QHBoxLayout *hLayout = new QHBoxLayout(); label = new QLabel(i18n("Password: "), mainWidget); hLayout->addWidget(label); KPasswordLineEdit *lineEdit = new KPasswordLineEdit(); hLayout->addWidget(lineEdit); vLayout->addLayout(hLayout); lineEdit->setFocus(); const int result = dlg->exec(); if (result == QDialog::Accepted && !dlg.isNull()) { password = lineEdit->password(); } delete dlg; return password; } void Settings::updateToV2() { // Take the first URL that was configured to get the username that // has the most chances being the default QStringList urls = remoteUrls(); if (urls.isEmpty()) { return; } QString urlConfigStr = urls.at(0); UrlConfiguration urlConfig(urlConfigStr); QRegExp regexp(QLatin1Char('^') + urlConfig.mUser); QMutableStringListIterator it(urls); while (it.hasNext()) { it.next(); it.value().replace(regexp, QStringLiteral("$default$")); } setDefaultUsername(urlConfig.mUser); QString key = urlConfig.mUrl + QLatin1Char(',') + KDAV::ProtocolInfo::protocolName(KDAV::Protocol(urlConfig.mProtocol)); QString pass = loadPassword(key, urlConfig.mUser); if (!pass.isNull()) { setDefaultPassword(pass); } setRemoteUrls(urls); setSettingsVersion(2); save(); } void Settings::updateToV3() { QStringList updatedUrls; foreach (const QString &url, remoteUrls()) { QStringList splitUrl = url.split(QLatin1Char('|')); if (splitUrl.size() == 3) { KDAV::Protocol protocol = Utils::protocolByTranslatedName(splitUrl.at(1)); splitUrl[1] = KDAV::ProtocolInfo::protocolName(protocol); updatedUrls << splitUrl.join(QLatin1Char('|')); } } setRemoteUrls(updatedUrls); setSettingsVersion(3); save(); } diff --git a/resources/dav/resource/setupwizard.cpp b/resources/dav/resource/setupwizard.cpp index 157dd23f8..8d4d16947 100644 --- a/resources/dav/resource/setupwizard.cpp +++ b/resources/dav/resource/setupwizard.cpp @@ -1,559 +1,559 @@ /* Copyright (c) 2010 Tobias Koenig 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. */ #include "setupwizard.h" #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include enum GroupwareServers { Citadel, DAVical, eGroupware, OpenGroupware, ScalableOGo, Scalix, Zarafa, Zimbra }; static QString settingsToUrl(const QWizard *wizard, const QString &protocol) { QString desktopFilePath = wizard->property("providerDesktopFilePath").toString(); if (desktopFilePath.isEmpty()) { return QString(); } KService::Ptr service = KService::serviceByStorageId(desktopFilePath); if (!service) { return QString(); } QStringList supportedProtocols = service->property(QStringLiteral("X-DavGroupware-SupportedProtocols")).toStringList(); if (!supportedProtocols.contains(protocol)) { return QString(); } QString pathPattern; QString pathPropertyName(QStringLiteral("X-DavGroupware-") + protocol + QStringLiteral("Path")); if (service->property(pathPropertyName).isNull()) { return QString(); } pathPattern.append(service->property(pathPropertyName).toString() + QLatin1Char('/')); QString username = wizard->field(QStringLiteral("credentialsUserName")).toString(); QString localPart(username); localPart.remove(QRegExp(QLatin1String("@.*$"))); pathPattern.replace(QLatin1String("$user$"), username); pathPattern.replace(QLatin1String("$localpart$"), localPart); QString providerName; if (!service->property(QStringLiteral("X-DavGroupware-Provider")).isNull()) { providerName = service->property(QStringLiteral("X-DavGroupware-Provider")).toString(); } QString localPath = wizard->field(QStringLiteral("installationPath")).toString(); if (!localPath.isEmpty()) { if (providerName == QLatin1String("davical")) { if (!localPath.endsWith(QLatin1Char('/'))) { pathPattern.append(localPath + QLatin1Char('/')); } else { pathPattern.append(localPath); } } else { if (!localPath.startsWith(QLatin1Char('/'))) { pathPattern.prepend(QLatin1Char('/') + localPath); } else { pathPattern.prepend(localPath); } } } QUrl url; if (!wizard->property("usePredefinedProvider").isNull()) { if (service->property(QStringLiteral("X-DavGroupware-ProviderUsesSSL")).toBool()) { url.setScheme(QStringLiteral("https")); } else { url.setScheme(QStringLiteral("http")); } QString hostPropertyName(QStringLiteral("X-DavGroupware-") + protocol + QStringLiteral("Host")); if (service->property(hostPropertyName).isNull()) { return QString(); } url.setHost(service->property(hostPropertyName).toString()); url.setPath(pathPattern); } else { if (wizard->field(QStringLiteral("connectionUseSecureConnection")).toBool()) { url.setScheme(QStringLiteral("https")); } else { url.setScheme(QStringLiteral("http")); } QString host = wizard->field(QStringLiteral("connectionHost")).toString(); if (host.isEmpty()) { return QString(); } QStringList hostParts = host.split(QLatin1Char(':')); url.setHost(hostParts.at(0)); url.setPath(pathPattern); if (hostParts.size() == 2) { int port = hostParts.at(1).toInt(); if (port) { url.setPort(port); } } } return url.toString(); } /* * SetupWizard */ SetupWizard::SetupWizard(QWidget *parent) : QWizard(parent) { setWindowTitle(i18n("DAV groupware configuration wizard")); setWindowIcon(QIcon::fromTheme(QStringLiteral("folder-remote"))); setPage(W_CredentialsPage, new CredentialsPage); setPage(W_PredefinedProviderPage, new PredefinedProviderPage); setPage(W_ServerTypePage, new ServerTypePage); setPage(W_ConnectionPage, new ConnectionPage); setPage(W_CheckPage, new CheckPage); } QString SetupWizard::displayName() const { QString desktopFilePath = property("providerDesktopFilePath").toString(); if (desktopFilePath.isEmpty()) { return QString(); } KService::Ptr service = KService::serviceByStorageId(desktopFilePath); if (!service) { return QString(); } return service->name(); } SetupWizard::Url::List SetupWizard::urls() const { Url::List urls; QString desktopFilePath = property("providerDesktopFilePath").toString(); if (desktopFilePath.isEmpty()) { return urls; } KService::Ptr service = KService::serviceByStorageId(desktopFilePath); if (!service) { return urls; } const QStringList supportedProtocols = service->property(QStringLiteral("X-DavGroupware-SupportedProtocols")).toStringList(); for (const QString &protocol : supportedProtocols) { Url url; if (protocol == QLatin1String("CalDav")) { url.protocol = KDAV::CalDav; } else if (protocol == QLatin1String("CardDav")) { url.protocol = KDAV::CardDav; } else if (protocol == QLatin1String("GroupDav")) { url.protocol = KDAV::GroupDav; } else { return urls; } QString urlStr = settingsToUrl(this, protocol); if (!urlStr.isEmpty()) { url.url = urlStr; url.userName = QStringLiteral("$default$"); urls << url; } } return urls; } /* * CredentialsPage */ CredentialsPage::CredentialsPage(QWidget *parent) : QWizardPage(parent) { setTitle(i18n("Login Credentials")); setSubTitle(i18n("Enter your credentials to login to the groupware server")); QFormLayout *layout = new QFormLayout(this); mUserName = new KLineEdit; layout->addRow(i18n("User:"), mUserName); registerField(QStringLiteral("credentialsUserName*"), mUserName); mPassword = new KPasswordLineEdit; layout->addRow(i18n("Password:"), mPassword); registerField(QStringLiteral("credentialsPassword*"), mPassword, "password", SIGNAL(passwordChanged(QString))); } int CredentialsPage::nextId() const { QString userName = field(QStringLiteral("credentialsUserName")).toString(); if (userName.endsWith(QLatin1String("@yahoo.com"))) { KService::List offers; offers = KServiceTypeTrader::self()->query(QStringLiteral("DavGroupwareProvider"), QStringLiteral("Name == 'Yahoo!'")); if (offers.isEmpty()) { return SetupWizard::W_ServerTypePage; } wizard()->setProperty("usePredefinedProvider", true); wizard()->setProperty("predefinedProviderName", offers.at(0)->name()); wizard()->setProperty("providerDesktopFilePath", offers.at(0)->entryPath()); return SetupWizard::W_PredefinedProviderPage; } else { return SetupWizard::W_ServerTypePage; } } /* * PredefinedProviderPage */ PredefinedProviderPage::PredefinedProviderPage(QWidget *parent) : QWizardPage(parent) { setTitle(i18n("Predefined provider found")); setSubTitle(i18n("Select if you want to use the auto-detected provider")); QVBoxLayout *layout = new QVBoxLayout(this); mLabel = new QLabel; layout->addWidget(mLabel); mProviderGroup = new QButtonGroup(this); mProviderGroup->setExclusive(true); mUseProvider = new QRadioButton; mProviderGroup->addButton(mUseProvider); mUseProvider->setChecked(true); layout->addWidget(mUseProvider); mDontUseProvider = new QRadioButton(i18n("No, choose another server")); mProviderGroup->addButton(mDontUseProvider); layout->addWidget(mDontUseProvider); } void PredefinedProviderPage::initializePage() { mLabel->setText(i18n("Based on the email address you used as a login, this wizard\n" "can configure automatically an account for %1 services.\n" "Do you wish to do so?", wizard()->property("predefinedProviderName").toString())); mUseProvider->setText(i18n("Yes, use %1 as provider", wizard()->property("predefinedProviderName").toString())); } int PredefinedProviderPage::nextId() const { if (mUseProvider->isChecked()) { return SetupWizard::W_CheckPage; } else { wizard()->setProperty("usePredefinedProvider", QVariant()); wizard()->setProperty("providerDesktopFilePath", QVariant()); return SetupWizard::W_ServerTypePage; } } /* * ServerTypePage */ bool compareServiceOffers(const QPair &off1, const QPair &off2) { return off1.first.toLower() < off2.first.toLower(); } ServerTypePage::ServerTypePage(QWidget *parent) : QWizardPage(parent) { setTitle(i18n("Groupware Server")); setSubTitle(i18n("Select the groupware server the resource shall be configured for")); mProvidersCombo = new QComboBox(this); mProvidersCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); KServiceTypeTrader *trader = KServiceTypeTrader::self(); const KService::List providers = trader->query(QStringLiteral("DavGroupwareProvider")); QList< QPair > offers; offers.reserve(providers.count()); for (const KService::Ptr &provider : providers) { offers.append(QPair(provider->name(), provider->entryPath())); } std::sort(offers.begin(), offers.end(), compareServiceOffers); QListIterator< QPair > it(offers); while (it.hasNext()) { QPair p = it.next(); mProvidersCombo->addItem(p.first, p.second); } registerField(QStringLiteral("provider"), mProvidersCombo, "currentText"); QVBoxLayout *layout = new QVBoxLayout(this); mServerGroup = new QButtonGroup(this); mServerGroup->setExclusive(true); QHBoxLayout *hLayout = new QHBoxLayout; QRadioButton *button = new QRadioButton(i18n("Use one of those servers:")); registerField(QStringLiteral("templateConfiguration"), button); mServerGroup->addButton(button); mServerGroup->setId(button, 0); button->setChecked(true); hLayout->addWidget(button); hLayout->addWidget(mProvidersCombo); layout->addLayout(hLayout); button = new QRadioButton(i18n("Configure the resource manually")); connect(button, &QRadioButton::toggled, this, &ServerTypePage::manualConfigToggled); registerField(QStringLiteral("manualConfiguration"), button); mServerGroup->addButton(button); mServerGroup->setId(button, 1); layout->addWidget(button); layout->addStretch(1); } void ServerTypePage::manualConfigToggled(bool state) { setFinalPage(state); wizard()->button(QWizard::NextButton)->setEnabled(!state); } bool ServerTypePage::validatePage() { QVariant desktopFilePath = mProvidersCombo->itemData(mProvidersCombo->currentIndex()); if (desktopFilePath.isNull()) { return false; } else { wizard()->setProperty("providerDesktopFilePath", desktopFilePath); return true; } } /* * ConnectionPage */ ConnectionPage::ConnectionPage(QWidget *parent) : QWizardPage(parent) , mPreviewLayout(nullptr) , mCalDavUrlPreview(nullptr) , mCardDavUrlPreview(nullptr) , mGroupDavUrlPreview(nullptr) { setTitle(i18n("Connection")); setSubTitle(i18n("Enter the connection information for the groupware server")); mLayout = new QFormLayout(this); QRegExp hostnameRegexp(QStringLiteral("^[a-z0-9][.a-z0-9-]*[a-z0-9](?::[0-9]+)?$")); mHost = new KLineEdit; registerField(QStringLiteral("connectionHost*"), mHost); mHost->setValidator(new QRegExpValidator(hostnameRegexp, this)); mLayout->addRow(i18n("Host"), mHost); mPath = new KLineEdit; mLayout->addRow(i18n("Installation path"), mPath); registerField(QStringLiteral("installationPath"), mPath); mUseSecureConnection = new QCheckBox(i18n("Use secure connection")); mUseSecureConnection->setChecked(true); registerField(QStringLiteral("connectionUseSecureConnection"), mUseSecureConnection); mLayout->addRow(QString(), mUseSecureConnection); connect(mHost, &KLineEdit::textChanged, this, &ConnectionPage::urlElementChanged); connect(mPath, &KLineEdit::textChanged, this, &ConnectionPage::urlElementChanged); connect(mUseSecureConnection, &QCheckBox::toggled, this, &ConnectionPage::urlElementChanged); } void ConnectionPage::initializePage() { KService::Ptr service = KService::serviceByStorageId(wizard()->property("providerDesktopFilePath").toString()); if (!service) { return; } QString providerInstallationPath = service->property(QStringLiteral("X-DavGroupware-InstallationPath")).toString(); if (!providerInstallationPath.isEmpty()) { mPath->setText(providerInstallationPath); } QStringList supportedProtocols = service->property(QStringLiteral("X-DavGroupware-SupportedProtocols")).toStringList(); mPreviewLayout = new QFormLayout; mLayout->addRow(mPreviewLayout); if (supportedProtocols.contains(QLatin1String("CalDav"))) { mCalDavUrlLabel = new QLabel(i18n("Final URL (CalDav)")); mCalDavUrlPreview = new QLabel; mPreviewLayout->addRow(mCalDavUrlLabel, mCalDavUrlPreview); } if (supportedProtocols.contains(QLatin1String("CardDav"))) { mCardDavUrlLabel = new QLabel(i18n("Final URL (CardDav)")); mCardDavUrlPreview = new QLabel; mPreviewLayout->addRow(mCardDavUrlLabel, mCardDavUrlPreview); } if (supportedProtocols.contains(QLatin1String("GroupDav"))) { mGroupDavUrlLabel = new QLabel(i18n("Final URL (GroupDav)")); mGroupDavUrlPreview = new QLabel; mPreviewLayout->addRow(mGroupDavUrlLabel, mGroupDavUrlPreview); } } void ConnectionPage::cleanupPage() { delete mPreviewLayout; if (mCalDavUrlPreview) { delete mCalDavUrlLabel; delete mCalDavUrlPreview; mCalDavUrlPreview = nullptr; } if (mCardDavUrlPreview) { delete mCardDavUrlLabel; delete mCardDavUrlPreview; mCardDavUrlPreview = nullptr; } if (mGroupDavUrlPreview) { delete mGroupDavUrlLabel; delete mGroupDavUrlPreview; mGroupDavUrlPreview = nullptr; } QWizardPage::cleanupPage(); } void ConnectionPage::urlElementChanged() { if (mHost->text().isEmpty()) { if (mCalDavUrlPreview) { mCalDavUrlPreview->setText(QStringLiteral("-")); } if (mCardDavUrlPreview) { mCardDavUrlPreview->setText(QStringLiteral("-")); } if (mGroupDavUrlPreview) { mGroupDavUrlPreview->setText(QStringLiteral("-")); } } else { if (mCalDavUrlPreview) { mCalDavUrlPreview->setText(settingsToUrl(this->wizard(), QStringLiteral("CalDav"))); } if (mCardDavUrlPreview) { mCardDavUrlPreview->setText(settingsToUrl(this->wizard(), QStringLiteral("CardDav"))); } if (mGroupDavUrlPreview) { mGroupDavUrlPreview->setText(settingsToUrl(this->wizard(), QStringLiteral("GroupDav"))); } } } /* * CheckPage */ CheckPage::CheckPage(QWidget *parent) : QWizardPage(parent) { setTitle(i18n("Test Connection")); setSubTitle(i18n("You can test now whether the groupware server can be accessed with the current configuration")); setFinalPage(true); QVBoxLayout *layout = new QVBoxLayout(this); QPushButton *button = new QPushButton(i18n("Test Connection")); layout->addWidget(button); mStatusLabel = new QTextBrowser; layout->addWidget(mStatusLabel); connect(button, &QRadioButton::clicked, this, &CheckPage::checkConnection); } void CheckPage::checkConnection() { mStatusLabel->clear(); KDAV::DavUrl::List davUrls; // convert list of SetupWizard::Url to list of KDAV::DavUrl const SetupWizard::Url::List urls = static_cast(wizard())->urls(); for (const SetupWizard::Url &url : urls) { KDAV::DavUrl davUrl; davUrl.setProtocol(url.protocol); QUrl serverUrl(url.url); serverUrl.setUserName(wizard()->field(QStringLiteral("credentialsUserName")).toString()); serverUrl.setPassword(wizard()->field(QStringLiteral("credentialsPassword")).toString()); davUrl.setUrl(serverUrl); davUrls << davUrl; } // start the dav collections fetch job to test connectivity KDAV::DavCollectionsMultiFetchJob *job = new KDAV::DavCollectionsMultiFetchJob(davUrls, this); connect(job, &KDAV::DavCollectionsMultiFetchJob::result, this, &CheckPage::onFetchDone); job->start(); } void CheckPage::onFetchDone(KJob *job) { QString msg; QPixmap icon; if (job->error()) { msg = i18n("An error occurred: %1", job->errorText()); icon = QIcon::fromTheme(QStringLiteral("dialog-close")).pixmap(16, 16); } else { msg = i18n("Connected successfully"); icon = QIcon::fromTheme(QStringLiteral("dialog-ok-apply")).pixmap(16, 16); } mStatusLabel->setHtml(QStringLiteral(" %1").arg(msg)); mStatusLabel->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("icon")), QVariant(icon)); } diff --git a/resources/imap/settings.cpp b/resources/imap/settings.cpp index db297268c..9b427f3ff 100644 --- a/resources/imap/settings.cpp +++ b/resources/imap/settings.cpp @@ -1,313 +1,313 @@ /* Copyright (c) 2008 Volker Krause Copyright (c) 2008 Omat Holding B.V. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "settings.h" #include "settingsadaptor.h" #include "imapaccount.h" #include -#include +#include using KWallet::Wallet; #include "imapresource_debug.h" #include #include #include #include /** * Maps the enum used to represent authentication in MailTransport (kdepimlibs) * to the one used by the imap resource. * @param authType the MailTransport auth enum value * @return the corresponding KIMAP auth value. * @note will cause fatal error if there is no mapping, so be careful not to pass invalid auth options (e.g., APOP) to this function. */ KIMAP::LoginJob::AuthenticationMode Settings::mapTransportAuthToKimap(MailTransport::Transport::EnumAuthenticationType::type authType) { // typedef these for readability typedef MailTransport::Transport::EnumAuthenticationType MTAuth; typedef KIMAP::LoginJob KIAuth; switch (authType) { case MTAuth::ANONYMOUS: return KIAuth::Anonymous; case MTAuth::PLAIN: return KIAuth::Plain; case MTAuth::NTLM: return KIAuth::NTLM; case MTAuth::LOGIN: return KIAuth::Login; case MTAuth::GSSAPI: return KIAuth::GSSAPI; case MTAuth::DIGEST_MD5: return KIAuth::DigestMD5; case MTAuth::CRAM_MD5: return KIAuth::CramMD5; case MTAuth::CLEAR: return KIAuth::ClearText; case MTAuth::XOAUTH2: return KIAuth::XOAuth2; default: qFatal("mapping from Transport::EnumAuthenticationType -> KIMAP::LoginJob::AuthenticationMode not possible"); } return KIAuth::ClearText; // dummy value, shouldn't get here. } Settings::Settings(WId winId) : SettingsBase() , m_winId(winId) { load(); new SettingsAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), this, QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents); } void Settings::setWinId(WId winId) { m_winId = winId; } void Settings::clearCachedPassword() { m_password.clear(); } void Settings::cleanup() { Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId); if (wallet && wallet->isOpen()) { if (wallet->hasFolder(QStringLiteral("imap"))) { wallet->setFolder(QStringLiteral("imap")); wallet->removeEntry(config()->name()); } delete wallet; } } void Settings::requestPassword() { if (!m_password.isEmpty() || (mapTransportAuthToKimap((MailTransport::TransportBase::EnumAuthenticationType::type)authentication()) == KIMAP::LoginJob::GSSAPI)) { Q_EMIT passwordRequestCompleted(m_password, false); } else { Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId, Wallet::Asynchronous); if (wallet) { connect(wallet, &KWallet::Wallet::walletOpened, this, &Settings::onWalletOpened); } else { QMetaObject::invokeMethod(this, "onWalletOpened", Qt::QueuedConnection, Q_ARG(bool, true)); } } } void Settings::onWalletOpened(bool success) { if (!success) { Q_EMIT passwordRequestCompleted(QString(), true); } else { Wallet *wallet = qobject_cast(sender()); bool passwordNotStoredInWallet = true; if (wallet && wallet->hasFolder(QStringLiteral("imap"))) { wallet->setFolder(QStringLiteral("imap")); wallet->readPassword(config()->name(), m_password); passwordNotStoredInWallet = false; } Q_EMIT passwordRequestCompleted(m_password, passwordNotStoredInWallet); if (wallet) { wallet->deleteLater(); } } } QString Settings::password(bool *userRejected) const { if (userRejected != nullptr) { *userRejected = false; } if (!m_password.isEmpty() || (mapTransportAuthToKimap((MailTransport::TransportBase::EnumAuthenticationType::type)authentication()) == KIMAP::LoginJob::GSSAPI)) { return m_password; } Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId); if (wallet && wallet->isOpen()) { if (wallet->hasFolder(QStringLiteral("imap"))) { wallet->setFolder(QStringLiteral("imap")); wallet->readPassword(config()->name(), m_password); } else { wallet->createFolder(QStringLiteral("imap")); } } else if (userRejected != nullptr) { *userRejected = true; } delete wallet; return m_password; } QString Settings::sieveCustomPassword(bool *userRejected) const { if (userRejected != nullptr) { *userRejected = false; } if (!m_customSievePassword.isEmpty()) { return m_customSievePassword; } Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId); if (wallet && wallet->isOpen()) { if (wallet->hasFolder(QStringLiteral("imap"))) { wallet->setFolder(QStringLiteral("imap")); wallet->readPassword(QStringLiteral("custom_sieve_") + config()->name(), m_customSievePassword); } else { wallet->createFolder(QStringLiteral("imap")); } } else if (userRejected != nullptr) { *userRejected = true; } delete wallet; return m_customSievePassword; } void Settings::setSieveCustomPassword(const QString &password) { if (m_customSievePassword == password) { return; } m_customSievePassword = password; Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId); if (wallet && wallet->isOpen()) { if (!wallet->hasFolder(QStringLiteral("imap"))) { wallet->createFolder(QStringLiteral("imap")); } wallet->setFolder(QStringLiteral("imap")); wallet->writePassword(QLatin1String("custom_sieve_") + config()->name(), password); qCDebug(IMAPRESOURCE_LOG) << "Wallet save: " << wallet->sync(); } delete wallet; } void Settings::setPassword(const QString &password) { if (password == m_password) { return; } if (mapTransportAuthToKimap((MailTransport::TransportBase::EnumAuthenticationType::type)authentication()) == KIMAP::LoginJob::GSSAPI) { return; } m_password = password; Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId); if (wallet && wallet->isOpen()) { if (!wallet->hasFolder(QStringLiteral("imap"))) { wallet->createFolder(QStringLiteral("imap")); } wallet->setFolder(QStringLiteral("imap")); wallet->writePassword(config()->name(), password); qCDebug(IMAPRESOURCE_LOG) << "Wallet save: " << wallet->sync(); } delete wallet; } void Settings::loadAccount(ImapAccount *account) const { account->setServer(imapServer()); if (imapPort() >= 0) { account->setPort(imapPort()); } account->setUserName(userName()); account->setSubscriptionEnabled(subscriptionEnabled()); account->setUseNetworkProxy(useProxy()); const QString encryption = safety(); if (encryption == QLatin1String("SSL")) { account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS); } else if (encryption == QLatin1String("STARTTLS")) { account->setEncryptionMode(KIMAP::LoginJob::STARTTLS); } else { account->setEncryptionMode(KIMAP::LoginJob::Unencrypted); } //Some SSL Server fail to advertise an ssl version they support (AnySslVersion), //we therefore allow overriding this in the config //(so we don't have to make the UI unnecessarily complex for properly working servers). const QString overrideEncryptionMode = overrideEncryption(); if (!overrideEncryptionMode.isEmpty()) { qCWarning(IMAPRESOURCE_LOG) << "Overriding encryption mode with: " << overrideEncryptionMode; if (overrideEncryptionMode == QLatin1String("SSLV2")) { account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS); } else if (overrideEncryptionMode == QLatin1String("SSLV3")) { account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS); } else if (overrideEncryptionMode == QLatin1String("TLSV1")) { account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS); } else if (overrideEncryptionMode == QLatin1String("SSL")) { account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS); } else if (overrideEncryptionMode == QLatin1String("STARTTLS")) { account->setEncryptionMode(KIMAP::LoginJob::STARTTLS); } else if (overrideEncryptionMode == QLatin1String("UNENCRYPTED")) { account->setEncryptionMode(KIMAP::LoginJob::Unencrypted); } else { qCWarning(IMAPRESOURCE_LOG) << "Tried to force invalid encryption mode: " << overrideEncryptionMode; } } account->setAuthenticationMode( mapTransportAuthToKimap( (MailTransport::TransportBase::EnumAuthenticationType::type)authentication() ) ); account->setTimeout(sessionTimeout()); } QString Settings::rootRemoteId() const { return QStringLiteral("imap://") + userName() + QLatin1Char('@') + imapServer() + QLatin1Char('/'); } void Settings::renameRootCollection(const QString &newName) { Akonadi::Collection rootCollection; rootCollection.setRemoteId(rootRemoteId()); Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(rootCollection, Akonadi::CollectionFetchJob::Base); fetchJob->setProperty("collectionName", newName); connect(fetchJob, &KJob::result, this, &Settings::onRootCollectionFetched); } void Settings::onRootCollectionFetched(KJob *job) { const QString newName = job->property("collectionName").toString(); Q_ASSERT(!newName.isEmpty()); Akonadi::CollectionFetchJob *fetchJob = static_cast(job); if (fetchJob->collections().size() == 1) { Akonadi::Collection rootCollection = fetchJob->collections().at(0); rootCollection.setName(newName); new Akonadi::CollectionModifyJob(rootCollection); // We don't care about the result here, nothing we can/should do if the renaming fails } } diff --git a/resources/pop3/accountwidget.cpp b/resources/pop3/accountwidget.cpp index 3681e614c..06a40d4f6 100644 --- a/resources/pop3/accountwidget.cpp +++ b/resources/pop3/accountwidget.cpp @@ -1,638 +1,638 @@ /* * 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 #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) : QWidget(parent) , mValidator(this) , mIdentifier(identifier) { 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()) { nameEdit->setText(i18n("POP3 Account")); } else { nameEdit->setText(Settings::self()->name()); } nameEdit->setFocus(); loginEdit->setText(!Settings::self()->login().isEmpty() ? Settings::self()->login() : KUser().loginName()); hostEdit->setText( !Settings::self()->host().isEmpty() ? Settings::self()->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(); 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()); slotEnableLeaveOnServerDays(leaveOnServerDaysCheck->isEnabled() ? Settings::self()->leaveOnServerDays() >= 1 : false); slotEnableLeaveOnServerCount(leaveOnServerCountCheck->isEnabled() ? Settings::self()->leaveOnServerCount() >= 1 : false); slotEnableLeaveOnServerSize(leaveOnServerSizeCheck->isEnabled() ? Settings::self()->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()); 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(int id) { 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() ? (leaveOnServerDaysCheck->isChecked() ? leaveOnServerDaysSpin->value() : -1) : 0); Settings::self()->setLeaveOnServerCount(leaveOnServerCheck->isChecked() ? (leaveOnServerCountCheck->isChecked() ? leaveOnServerCountSpin->value() : -1) : 0); Settings::self()->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(); // 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/pop3resource.cpp b/resources/pop3/pop3resource.cpp index 55afef5df..4042809df 100644 --- a/resources/pop3/pop3resource.cpp +++ b/resources/pop3/pop3resource.cpp @@ -1,1046 +1,1046 @@ /* 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 #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)) { new Settings(KSharedConfig::openConfig()); Akonadi::AttributeFactory::registerAttribute(); setNeedsNetwork(true); Settings::self()->setResourceId(identifier()); if (Settings::self()->name().isEmpty()) { if (name() == identifier()) { Settings::self()->setName(i18n("POP3 Account")); } else { Settings::self()->setName(name()); } } setName(Settings::self()->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(); 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); } 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->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(); 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()); 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); 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(); advanceState(Connect); break; } const bool passwordNeeded = Settings::self()->authenticationMethod() != MailTransport::Transport::EnumAuthenticationType::GSSAPI; const bool loadPasswordFromWallet = !mAskAgain && passwordNeeded && !Settings::self()->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()) { 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); 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(); 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); 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()) +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) { // 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()) { const int alreadyDownloadedID = mUidsToIdsMap.value(alreadyDownloadedUID, -1); if (alreadyDownloadedID != -1) { bytesRemainingOnServer += mIdsToSizeMap.value(alreadyDownloadedID); } } if (Settings::self()->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(); 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()) { idsToDeleteFromServer = mIdsToSizeMap.keys(); if (!mIdsToSaveValid) { mIdsToSaveValid = true; mIdsToSave.clear(); const QSet idsOnServer = QSet::fromList(idsToDeleteFromServer); // If the time-limited leave rule is checked, add the newer messages to // the list of messages to keep if (Settings::self()->leaveOnServerDays() > 0) { const int secondsPerDay = 86400; time_t timeLimit = time(nullptr) - (secondsPerDay * Settings::self()->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 (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); 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(); 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(); 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(); // // 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(); } 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/settings.cpp b/resources/pop3/settings.cpp index 97fc49237..1f91ef30e 100644 --- a/resources/pop3/settings.cpp +++ b/resources/pop3/settings.cpp @@ -1,85 +1,85 @@ /* 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 #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) : SettingsBase(config) { Q_ASSERT(!s_globalSettings->q); s_globalSettings->q = this; 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; }