File indexing completed on 2025-02-16 04:50:21

0001 /*
0002     SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2008 Omat Holding B.V. <info@omat.nl>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "settings.h"
0009 #include "settingsadaptor.h"
0010 #include <qt6keychain/keychain.h>
0011 using namespace QKeychain;
0012 #include "imapaccount.h"
0013 #include <config-imap.h>
0014 
0015 #include <KWallet>
0016 using KWallet::Wallet;
0017 
0018 #include "imapresource_debug.h"
0019 
0020 #include <QDBusConnection>
0021 
0022 #include <Akonadi/Collection>
0023 #include <Akonadi/CollectionFetchJob>
0024 #include <Akonadi/CollectionModifyJob>
0025 
0026 /**
0027  * Maps the enum used to represent authentication in MailTransport (kdepimlibs)
0028  * to the one used by the imap resource.
0029  * @param authType the MailTransport auth enum value
0030  * @return the corresponding KIMAP auth value.
0031  * @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.
0032  */
0033 KIMAP::LoginJob::AuthenticationMode Settings::mapTransportAuthToKimap(MailTransport::Transport::EnumAuthenticationType authType)
0034 {
0035     // typedef these for readability
0036     using MTAuth = MailTransport::Transport::EnumAuthenticationType;
0037     using KIAuth = KIMAP::LoginJob;
0038     switch (authType) {
0039     case MTAuth::ANONYMOUS:
0040         return KIAuth::Anonymous;
0041     case MTAuth::PLAIN:
0042         return KIAuth::Plain;
0043     case MTAuth::NTLM:
0044         return KIAuth::NTLM;
0045     case MTAuth::LOGIN:
0046         return KIAuth::Login;
0047     case MTAuth::GSSAPI:
0048         return KIAuth::GSSAPI;
0049     case MTAuth::DIGEST_MD5:
0050         return KIAuth::DigestMD5;
0051     case MTAuth::CRAM_MD5:
0052         return KIAuth::CramMD5;
0053     case MTAuth::CLEAR:
0054         return KIAuth::ClearText;
0055     case MTAuth::XOAUTH2:
0056         return KIAuth::XOAuth2;
0057     default:
0058         qFatal("mapping from Transport::EnumAuthenticationType ->  KIMAP::LoginJob::AuthenticationMode not possible");
0059     }
0060     return KIAuth::ClearText; // dummy value, shouldn't get here.
0061 }
0062 
0063 Settings::Settings(WId winId)
0064     : SettingsBase()
0065     , m_winId(winId)
0066 {
0067     load();
0068 
0069     new SettingsAdaptor(this);
0070     QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"),
0071                                                  this,
0072                                                  QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents);
0073 }
0074 
0075 void Settings::setWinId(WId winId)
0076 {
0077     m_winId = winId;
0078 }
0079 
0080 void Settings::clearCachedPassword()
0081 {
0082     m_password.clear();
0083 }
0084 
0085 void Settings::cleanup()
0086 {
0087     auto deleteJob = new DeletePasswordJob(QStringLiteral("imap"));
0088     deleteJob->setKey(config()->name());
0089     deleteJob->start();
0090 }
0091 
0092 void Settings::requestPassword()
0093 {
0094     if (!m_password.isEmpty() || (mapTransportAuthToKimap((MailTransport::TransportBase::EnumAuthenticationType)authentication()) == KIMAP::LoginJob::GSSAPI)) {
0095         Q_EMIT passwordRequestCompleted(m_password, false);
0096     } else {
0097         // Already async. Just port to ReadPassword
0098         Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId, Wallet::Asynchronous);
0099         if (wallet) {
0100             connect(wallet, &KWallet::Wallet::walletOpened, this, &Settings::onWalletOpened);
0101         } else {
0102             QMetaObject::invokeMethod(this, "onWalletOpened", Qt::QueuedConnection, Q_ARG(bool, true));
0103         }
0104     }
0105 }
0106 
0107 void Settings::onWalletOpened(bool success)
0108 {
0109     if (!success) {
0110         Q_EMIT passwordRequestCompleted(QString(), true);
0111     } else {
0112         auto wallet = qobject_cast<Wallet *>(sender());
0113         bool passwordNotStoredInWallet = true;
0114         if (wallet && wallet->hasFolder(QStringLiteral("imap"))) {
0115             wallet->setFolder(QStringLiteral("imap"));
0116             wallet->readPassword(config()->name(), m_password);
0117             passwordNotStoredInWallet = false;
0118         }
0119 
0120         Q_EMIT passwordRequestCompleted(m_password, passwordNotStoredInWallet);
0121 
0122         if (wallet) {
0123             wallet->deleteLater();
0124         }
0125     }
0126 }
0127 
0128 QString Settings::password(bool *userRejected) const
0129 {
0130     if (userRejected != nullptr) {
0131         *userRejected = false;
0132     }
0133 
0134     if (!m_password.isEmpty() || (mapTransportAuthToKimap((MailTransport::TransportBase::EnumAuthenticationType)authentication()) == KIMAP::LoginJob::GSSAPI)) {
0135         return m_password;
0136     }
0137     // Move as async
0138     Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId);
0139     if (wallet && wallet->isOpen()) {
0140         if (wallet->hasFolder(QStringLiteral("imap"))) {
0141             wallet->setFolder(QStringLiteral("imap"));
0142             wallet->readPassword(config()->name(), m_password);
0143         } else {
0144             wallet->createFolder(QStringLiteral("imap"));
0145         }
0146     } else if (userRejected != nullptr) {
0147         *userRejected = true;
0148     }
0149     delete wallet;
0150     return m_password;
0151 }
0152 
0153 QString Settings::sieveCustomPassword(bool *userRejected) const
0154 {
0155     if (userRejected != nullptr) {
0156         *userRejected = false;
0157     }
0158 
0159     if (!m_customSievePassword.isEmpty()) {
0160         return m_customSievePassword;
0161     }
0162 
0163     // Move as async
0164     Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId);
0165     if (wallet && wallet->isOpen()) {
0166         if (wallet->hasFolder(QStringLiteral("imap"))) {
0167             wallet->setFolder(QStringLiteral("imap"));
0168             wallet->readPassword(QStringLiteral("custom_sieve_") + config()->name(), m_customSievePassword);
0169         } else {
0170             wallet->createFolder(QStringLiteral("imap"));
0171         }
0172     } else if (userRejected != nullptr) {
0173         *userRejected = true;
0174     }
0175     delete wallet;
0176     return m_customSievePassword;
0177 }
0178 
0179 void Settings::setSieveCustomPassword(const QString &password)
0180 {
0181     if (m_customSievePassword == password) {
0182         return;
0183     }
0184     m_customSievePassword = password;
0185     Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId);
0186     if (wallet && wallet->isOpen()) {
0187         if (!wallet->hasFolder(QStringLiteral("imap"))) {
0188             wallet->createFolder(QStringLiteral("imap"));
0189         }
0190         wallet->setFolder(QStringLiteral("imap"));
0191         wallet->writePassword(QLatin1StringView("custom_sieve_") + config()->name(), password);
0192         qCDebug(IMAPRESOURCE_LOG) << "Wallet save: " << wallet->sync();
0193     }
0194     delete wallet;
0195 }
0196 
0197 void Settings::setPassword(const QString &password)
0198 {
0199     if (password == m_password) {
0200         return;
0201     }
0202 
0203     if (mapTransportAuthToKimap((MailTransport::TransportBase::EnumAuthenticationType)authentication()) == KIMAP::LoginJob::GSSAPI) {
0204         return;
0205     }
0206 
0207     m_password = password;
0208     Wallet *wallet = Wallet::openWallet(Wallet::NetworkWallet(), m_winId);
0209     if (wallet && wallet->isOpen()) {
0210         if (!wallet->hasFolder(QStringLiteral("imap"))) {
0211             wallet->createFolder(QStringLiteral("imap"));
0212         }
0213         wallet->setFolder(QStringLiteral("imap"));
0214         wallet->writePassword(config()->name(), password);
0215         qCDebug(IMAPRESOURCE_LOG) << "Wallet save: " << wallet->sync();
0216     }
0217     delete wallet;
0218 }
0219 
0220 void Settings::loadAccount(ImapAccount *account) const
0221 {
0222     account->setServer(imapServer());
0223     if (imapPort() >= 0) {
0224         account->setPort(imapPort());
0225     }
0226 
0227     account->setUserName(userName());
0228     account->setSubscriptionEnabled(subscriptionEnabled());
0229     account->setUseNetworkProxy(useProxy());
0230 
0231     const QString encryption = safety();
0232     if (encryption == QLatin1StringView("SSL")) {
0233         account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS);
0234     } else if (encryption == QLatin1StringView("STARTTLS")) {
0235         account->setEncryptionMode(KIMAP::LoginJob::STARTTLS);
0236     } else {
0237         account->setEncryptionMode(KIMAP::LoginJob::Unencrypted);
0238     }
0239 
0240     // Some SSL Server fail to advertise an ssl version they support (AnySslVersion),
0241     // we therefore allow overriding this in the config
0242     //(so we don't have to make the UI unnecessarily complex for properly working servers).
0243     const QString overrideEncryptionMode = overrideEncryption();
0244     if (!overrideEncryptionMode.isEmpty()) {
0245         qCWarning(IMAPRESOURCE_LOG) << "Overriding encryption mode with: " << overrideEncryptionMode;
0246         if (overrideEncryptionMode == QLatin1StringView("SSLV2")) {
0247             account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS);
0248         } else if (overrideEncryptionMode == QLatin1StringView("SSLV3")) {
0249             account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS);
0250         } else if (overrideEncryptionMode == QLatin1StringView("TLSV1")) {
0251             account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS);
0252         } else if (overrideEncryptionMode == QLatin1StringView("SSL")) {
0253             account->setEncryptionMode(KIMAP::LoginJob::SSLorTLS);
0254         } else if (overrideEncryptionMode == QLatin1StringView("STARTTLS")) {
0255             account->setEncryptionMode(KIMAP::LoginJob::STARTTLS);
0256         } else if (overrideEncryptionMode == QLatin1StringView("UNENCRYPTED")) {
0257             account->setEncryptionMode(KIMAP::LoginJob::Unencrypted);
0258         } else {
0259             qCWarning(IMAPRESOURCE_LOG) << "Tried to force invalid encryption mode: " << overrideEncryptionMode;
0260         }
0261     }
0262 
0263     account->setAuthenticationMode(mapTransportAuthToKimap(static_cast<MailTransport::TransportBase::EnumAuthenticationType>(authentication())));
0264 
0265     account->setTimeout(sessionTimeout());
0266 }
0267 
0268 QString Settings::rootRemoteId() const
0269 {
0270     return QStringLiteral("imap://") + userName() + QLatin1Char('@') + imapServer() + QLatin1Char('/');
0271 }
0272 
0273 void Settings::renameRootCollection(const QString &newName)
0274 {
0275     Akonadi::Collection rootCollection;
0276     rootCollection.setRemoteId(rootRemoteId());
0277     auto fetchJob = new Akonadi::CollectionFetchJob(rootCollection, Akonadi::CollectionFetchJob::Base);
0278     fetchJob->setProperty("collectionName", newName);
0279     connect(fetchJob, &KJob::result, this, &Settings::onRootCollectionFetched);
0280 }
0281 
0282 void Settings::onRootCollectionFetched(KJob *job)
0283 {
0284     const QString newName = job->property("collectionName").toString();
0285     Q_ASSERT(!newName.isEmpty());
0286     auto fetchJob = static_cast<Akonadi::CollectionFetchJob *>(job);
0287     if (fetchJob->collections().size() == 1) {
0288         Akonadi::Collection rootCollection = fetchJob->collections().at(0);
0289         rootCollection.setName(newName);
0290         new Akonadi::CollectionModifyJob(rootCollection);
0291         // We don't care about the result here, nothing we can/should do if the renaming fails
0292     }
0293 }
0294 
0295 #include "moc_settings.cpp"