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"