File indexing completed on 2024-12-15 04:55:36

0001 /*
0002    SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "importimapsettingsthunderbirdcheckjob.h"
0008 #include "sieveeditor_debug.h"
0009 #include <KConfig>
0010 #include <KConfigGroup>
0011 #include <KLocalizedString>
0012 #include <QDir>
0013 #include <QFile>
0014 #include <QRegularExpression>
0015 
0016 LIBSIEVEEDITOR_EXPORT QString sieveeditor_thunderbird_default_toplevel_path = QDir::homePath() + QLatin1StringView("/.thunderbird/");
0017 
0018 ImportImapSettingsThunderbirdCheckJob::ImportImapSettingsThunderbirdCheckJob(QObject *parent)
0019     : AbstractImapSettingsCheckJob(parent)
0020 {
0021 }
0022 
0023 ImportImapSettingsThunderbirdCheckJob::~ImportImapSettingsThunderbirdCheckJob() = default;
0024 
0025 // Copy from mailimporter
0026 QMap<QString, QString> ImportImapSettingsThunderbirdCheckJob::listProfile(QString &currentProfile, const QString &defaultSettingPath)
0027 {
0028     const QString thunderbirdPath = defaultSettingPath + QLatin1StringView("/profiles.ini");
0029     QMap<QString, QString> lstProfile;
0030     if (QFileInfo::exists(thunderbirdPath)) {
0031         // ini file.
0032         KConfig config(thunderbirdPath);
0033         const QStringList profileList = config.groupList().filter(QRegularExpression(QStringLiteral("Profile\\d+")));
0034         const bool uniqProfile = (profileList.count() == 1);
0035         if (uniqProfile) {
0036             KConfigGroup group = config.group(profileList.at(0));
0037             const QString path = group.readEntry("Path");
0038             const QString name = group.readEntry(QStringLiteral("Name"));
0039             currentProfile = path;
0040             lstProfile.insert(name, path);
0041             return lstProfile;
0042         } else {
0043             for (const QString &profileName : profileList) {
0044                 KConfigGroup group = config.group(profileName);
0045                 const QString path = group.readEntry("Path");
0046                 const QString name = group.readEntry(QStringLiteral("Name"));
0047                 if (group.hasKey("Default") && (group.readEntry("Default", 0) == 1)) {
0048                     currentProfile = path;
0049                 }
0050                 lstProfile.insert(name, path);
0051             }
0052         }
0053     }
0054     return lstProfile;
0055 }
0056 
0057 void ImportImapSettingsThunderbirdCheckJob::start()
0058 {
0059     mSettingsWereImported = false;
0060     QString currentProfile;
0061     const QMap<QString, QString> lstProfile = listProfile(currentProfile, defaultPath());
0062     const int numberLstProfileCount = lstProfile.count();
0063     if (numberLstProfileCount > 0) {
0064         if (numberLstProfileCount == 1) {
0065             mSettingsWereImported = importSettings(defaultPath(), lstProfile.first());
0066         } else {
0067             QMap<QString, QString>::const_iterator i = lstProfile.constBegin();
0068             while (i != lstProfile.constEnd()) {
0069                 const bool imported = importSettings(defaultPath(), i.value());
0070                 if (imported) {
0071                     mSettingsWereImported = true;
0072                 }
0073                 ++i;
0074             }
0075         }
0076     }
0077     checkNoSettingsImported();
0078 }
0079 
0080 bool ImportImapSettingsThunderbirdCheckJob::importSettings(const QString &directory, const QString &defaultProfile)
0081 {
0082     const QString filePath = directory + QLatin1Char('/') + defaultProfile + QStringLiteral("/prefs.js");
0083     // qCDebug(SIEVEEDITOR_LOG) << "importSettings filename:" << filePath;
0084     QFile file(filePath);
0085     if (!file.open(QIODevice::ReadOnly)) {
0086         qCWarning(SIEVEEDITOR_LOG) << "Unable to open file " << filePath;
0087         return false;
0088     }
0089     QTextStream stream(&file);
0090     while (!stream.atEnd()) {
0091         const QString line = stream.readLine();
0092         if (line.startsWith(QLatin1StringView("user_pref"))) {
0093             if (line.contains(QLatin1StringView("mail.server.")) || line.contains(QLatin1StringView("mail.account."))
0094                 || line.contains(QLatin1StringView("mail.accountmanager.")) || line.contains(QLatin1StringView("extensions.sieve.account."))) {
0095                 insertIntoMap(line);
0096             }
0097         } else {
0098             if (!line.startsWith(QLatin1Char('#')) && line.isEmpty() && line.startsWith(QLatin1StringView("/*")) && line.startsWith(QLatin1StringView(" */"))
0099                 && line.startsWith(QLatin1StringView(" *"))) {
0100                 qCDebug(SIEVEEDITOR_LOG) << " unstored line :" << line;
0101             }
0102         }
0103     }
0104     const QString mailAccountPreference = mHashConfig.value(QStringLiteral("mail.accountmanager.accounts")).toString();
0105     if (mailAccountPreference.isEmpty()) {
0106         qCDebug(SIEVEEDITOR_LOG) << "No account found";
0107         return false;
0108     }
0109     const QStringList accountList = mailAccountPreference.split(QLatin1Char(','));
0110 
0111     bool atLeastAnAccountFound = false;
0112     for (const QString &account : accountList) {
0113         const QString serverName = mHashConfig.value(QStringLiteral("mail.account.%1").arg(account) + QStringLiteral(".server")).toString();
0114         const QString accountName = QStringLiteral("mail.server.%1").arg(serverName);
0115         const QString type = mHashConfig.value(accountName + QStringLiteral(".type")).toString();
0116         if (type == QLatin1StringView("imap")) {
0117             // Sieve settings == username@account
0118 
0119             const QString imapServerName = mHashConfig.value(accountName + QStringLiteral(".hostname")).toString();
0120             QString userName = mHashConfig.value(accountName + QStringLiteral(".userName")).toString();
0121             // Convert @ in username in %40
0122             QString userNameSieveConverted = userName;
0123             userNameSieveConverted.replace(QLatin1Char('@'), QStringLiteral("%40"));
0124             const QString sieveKeyServerUserName = QLatin1StringView("extensions.sieve.account.") + userNameSieveConverted + QLatin1Char('@') + imapServerName;
0125             // user_pref("extensions.sieve.account.<username>@<server>.enabled", true);
0126             if (mHashConfig.value(sieveKeyServerUserName + QStringLiteral(".enabled"), false).toBool()) {
0127                 // TODO
0128                 // user_pref("extensions.sieve.account.<username>@<server>.TLS", true);
0129                 // user_pref("extensions.sieve.account.<username>@<server>.TLS.forced", true);
0130                 // user_pref("extensions.sieve.account.<username>@<server>.activeAuthorization", 1);
0131                 // user_pref("extensions.sieve.account.<username>@<server>.activeHost", 1);
0132                 // user_pref("extensions.sieve.account.<username>@<server>.activeLogin", 1);
0133                 SieveEditorUtil::SieveServerConfig config;
0134                 // 0 4190
0135                 // 1 2000
0136                 // 2 custom
0137                 // Default == 4190
0138                 // user_pref("extensions.sieve.account.<username>@<server>.port", 1255);
0139                 config.sieveSettings.port = mHashConfig.value(sieveKeyServerUserName + QStringLiteral(".port"), 4190).toInt();
0140                 // not necessary to import this one : user_pref("extensions.sieve.account.<username>@<server>.port.type", 1);
0141 
0142                 // user_pref("extensions.sieve.account.<username>@<server>.hostname", "sdfsfsqsdf");
0143                 const QString sieveHostName = mHashConfig.value(sieveKeyServerUserName + QStringLiteral(".hostname")).toString();
0144                 if (sieveHostName.isEmpty()) {
0145                     config.sieveSettings.serverName = imapServerName;
0146                 } else {
0147                     config.sieveSettings.serverName = sieveHostName;
0148                 }
0149 
0150                 const QString sieveUserName = mHashConfig.value(sieveKeyServerUserName + QStringLiteral(".login.username")).toString();
0151                 // user_pref("extensions.sieve.account.<username>@<server>.login.username", "newuser");
0152                 if (sieveUserName.isEmpty()) {
0153                     config.sieveSettings.userName = userName;
0154                 } else {
0155                     config.sieveSettings.userName = sieveUserName;
0156                 }
0157 
0158                 // not necessary to import this one : user_pref("extensions.sieve.account.<username>@<server>.proxy.type", 1);
0159 
0160                 // qCDebug(SIEVEEDITOR_LOG) << "imap account " << accountName;
0161                 const QString name = mHashConfig.value(accountName + QStringLiteral(".name")).toString();
0162                 bool found;
0163                 const int sievePort = mHashConfig.value(accountName + QStringLiteral(".port")).toInt(&found);
0164                 if (found) {
0165                     config.sieveImapAccountSettings.setPort(sievePort);
0166                 }
0167                 encryption(config, accountName);
0168                 addAuth(config, accountName);
0169                 config.sieveImapAccountSettings.setUserName(userName);
0170                 config.sieveImapAccountSettings.setServerName(imapServerName);
0171 
0172                 if (config.isValid()) {
0173                     atLeastAnAccountFound = true;
0174                     Q_EMIT importSetting(name, config);
0175                 }
0176             }
0177         } else {
0178             // qCDebug(SIEVEEDITOR_LOG) << "Account " << accountName << " is not a imap account. Skip it.";
0179         }
0180     }
0181     return atLeastAnAccountFound;
0182 }
0183 
0184 // Stolen from import-wizard
0185 void ImportImapSettingsThunderbirdCheckJob::encryption(SieveEditorUtil::SieveServerConfig &config, const QString &accountName)
0186 {
0187     bool found;
0188     const int socketType = mHashConfig.value(accountName + QStringLiteral(".socketType")).toInt(&found);
0189     if (found) {
0190         switch (socketType) {
0191         case 0:
0192             // None
0193             config.sieveImapAccountSettings.setEncryptionMode(KSieveCore::SieveImapAccountSettings::EncryptionMode::Unencrypted);
0194             break;
0195         case 2:
0196             // STARTTLS
0197             config.sieveImapAccountSettings.setEncryptionMode(KSieveCore::SieveImapAccountSettings::EncryptionMode::STARTTLS);
0198             break;
0199         case 3:
0200             // SSL/TLS
0201             config.sieveImapAccountSettings.setEncryptionMode(KSieveCore::SieveImapAccountSettings::EncryptionMode::SSLorTLS);
0202             break;
0203         default:
0204             qCDebug(SIEVEEDITOR_LOG) << "Unknown encryption mode " << socketType;
0205         }
0206     } else {
0207         qCDebug(SIEVEEDITOR_LOG) << "Invalid encryption mode settings";
0208     }
0209 }
0210 
0211 void ImportImapSettingsThunderbirdCheckJob::addAuth(SieveEditorUtil::SieveServerConfig &config, const QString &accountName)
0212 {
0213     bool found = false;
0214     if (mHashConfig.contains(accountName + QStringLiteral(".authMethod"))) {
0215         const int authMethod = mHashConfig.value(accountName + QStringLiteral(".authMethod")).toInt(&found);
0216         if (found) {
0217             switch (authMethod) {
0218             case 0:
0219                 config.sieveImapAccountSettings.setAuthenticationType(KSieveCore::SieveImapAccountSettings::Plain);
0220                 break;
0221             case 4: // Encrypted password ???
0222                 config.sieveImapAccountSettings.setAuthenticationType(KSieveCore::SieveImapAccountSettings::Login);
0223                 qCDebug(SIEVEEDITOR_LOG) << " authmethod == encrypt password";
0224                 break;
0225             case 5: // GSSAPI
0226                 config.sieveImapAccountSettings.setAuthenticationType(KSieveCore::SieveImapAccountSettings::GSSAPI);
0227                 break;
0228             case 6: // NTLM
0229                 config.sieveImapAccountSettings.setAuthenticationType(KSieveCore::SieveImapAccountSettings::NTLM);
0230                 break;
0231             case 7: // TLS
0232                 qCDebug(SIEVEEDITOR_LOG) << " authmethod method == TLS"; //????
0233                 break;
0234             case 10: // OAuth2
0235                 config.sieveImapAccountSettings.setAuthenticationType(KSieveCore::SieveImapAccountSettings::XOAuth2);
0236                 qCDebug(SIEVEEDITOR_LOG) << " authmethod method == OAuth2"; //????
0237                 break;
0238             default:
0239                 qCDebug(SIEVEEDITOR_LOG) << " ImportImapSettingsThunderbirdCheckJob::addAuth unknown :" << authMethod;
0240                 break;
0241             }
0242         } else {
0243             qCDebug(SIEVEEDITOR_LOG) << " ImportImapSettingsThunderbirdCheckJob::addAuth undefine value";
0244         }
0245     }
0246 }
0247 
0248 void ImportImapSettingsThunderbirdCheckJob::insertIntoMap(const QString &line)
0249 {
0250     QString newLine = line;
0251     newLine.remove(QStringLiteral("user_pref(\""));
0252     newLine.remove(QStringLiteral(");"));
0253     const int pos = newLine.indexOf(QLatin1Char(','));
0254     QString key = newLine.left(pos);
0255     key.remove(key.length() - 1, 1);
0256     QString valueStr = newLine.right(newLine.length() - pos - 2);
0257     if (valueStr.at(0) == QLatin1Char('"')) {
0258         valueStr.remove(0, 1);
0259         const int pos(valueStr.length() - 1);
0260         if (valueStr.at(pos) == QLatin1Char('"')) {
0261             valueStr.remove(pos, 1);
0262         }
0263         // Store as String
0264         mHashConfig.insert(key, valueStr);
0265     } else {
0266         if (valueStr == QLatin1StringView("true")) {
0267             mHashConfig.insert(key, true);
0268         } else if (valueStr == QLatin1StringView("false")) {
0269             mHashConfig.insert(key, false);
0270         } else {
0271             // Store as integer
0272             const int value = valueStr.toInt();
0273             mHashConfig.insert(key, value);
0274         }
0275     }
0276 }
0277 
0278 QString ImportImapSettingsThunderbirdCheckJob::defaultPath() const
0279 {
0280     return sieveeditor_thunderbird_default_toplevel_path;
0281 }
0282 
0283 bool ImportImapSettingsThunderbirdCheckJob::settingsCanBeImported() const
0284 {
0285     const QDir dir(defaultPath());
0286     return dir.exists();
0287 }
0288 
0289 QString ImportImapSettingsThunderbirdCheckJob::name() const
0290 {
0291     return i18n("Thunderbird IMAP Settings");
0292 }
0293 
0294 #include "moc_importimapsettingsthunderbirdcheckjob.cpp"