File indexing completed on 2024-09-22 04:50:04

0001 /*
0002   SPDX-FileCopyrightText: 2007 Till Adam <adam@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "filterimporterexporter.h"
0008 #include "dialog/selectthunderbirdfilterfilesdialog.h"
0009 #include "filterimporter/filterimporterbalsa.h"
0010 #include "filterimporter/filterimporterclawsmail.h"
0011 #include "filterimporter/filterimporterevolution.h"
0012 #include "filterimporter/filterimportergmail.h"
0013 #include "filterimporter/filterimporterprocmail.h"
0014 #include "filterimporter/filterimportersylpheed.h"
0015 #include "filterimporter/filterimporterthunderbird.h"
0016 #include "filterselectiondialog.h"
0017 #include "mailcommon_debug.h"
0018 #include "mailfilter.h"
0019 
0020 #include <MessageViewer/MessageViewerUtil>
0021 
0022 #include <KConfig>
0023 #include <KConfigGroup>
0024 #include <KListWidgetSearchLine>
0025 #include <KMessageBox>
0026 #include <QFileDialog>
0027 #include <QPointer>
0028 #include <QPushButton>
0029 #include <QRegularExpression>
0030 
0031 using namespace MailCommon;
0032 
0033 QList<MailFilter *> FilterImporterExporter::readFiltersFromConfig(const KSharedConfig::Ptr &config, QStringList &emptyFilters)
0034 {
0035     const KConfigGroup group = config->group("General");
0036 
0037     const int numFilters = group.readEntry("filters", 0);
0038 
0039     bool filterNeedUpdate = false;
0040     QList<MailFilter *> filters;
0041     for (int i = 0; i < numFilters; ++i) {
0042         const QString groupName = QStringLiteral("Filter #%1").arg(i);
0043 
0044         const KConfigGroup group = config->group(groupName);
0045         bool update = false;
0046         auto filter = new MailFilter(group, true /*interactive*/, update);
0047         filter->purify();
0048         if (update) {
0049             filterNeedUpdate = true;
0050         }
0051         if (filter->isEmpty()) {
0052             qCDebug(MAILCOMMON_LOG) << "Filter" << filter->asString() << "is empty!";
0053             emptyFilters << filter->name();
0054             delete filter;
0055         } else {
0056             filters.append(filter);
0057         }
0058     }
0059     if (filterNeedUpdate) {
0060         KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("akonadi_mailfilter_agentrc"));
0061 
0062         // Now, write out the new stuff:
0063         FilterImporterExporter::writeFiltersToConfig(filters, config);
0064         KConfigGroup group = config->group("General");
0065         group.sync();
0066     }
0067     return filters;
0068 }
0069 
0070 void FilterImporterExporter::writeFiltersToConfig(const QList<MailFilter *> &filters, KSharedConfig::Ptr config, bool exportFiler)
0071 {
0072     // first, delete all filter groups:
0073     const QStringList filterGroups = config->groupList().filter(QRegularExpression(QStringLiteral("Filter #\\d+")));
0074 
0075     for (const QString &group : filterGroups) {
0076         config->deleteGroup(group);
0077     }
0078 
0079     int i = 0;
0080     for (const MailFilter *filter : filters) {
0081         if (!filter->isEmpty()) {
0082             const QString groupName = QStringLiteral("Filter #%1").arg(i);
0083 
0084             KConfigGroup group = config->group(groupName);
0085             filter->writeConfig(group, exportFiler);
0086             ++i;
0087         }
0088     }
0089 
0090     KConfigGroup group = config->group("General");
0091     group.writeEntry("filters", i);
0092 
0093     config->sync();
0094 }
0095 
0096 class Q_DECL_HIDDEN FilterImporterExporter::FilterImporterExporterPrivate
0097 {
0098 public:
0099     FilterImporterExporterPrivate(QWidget *parent)
0100         : mParent(parent)
0101     {
0102     }
0103 
0104     void warningInfoAboutInvalidFilter(const QStringList &emptyFilters) const;
0105     QWidget *const mParent;
0106 };
0107 
0108 void FilterImporterExporter::FilterImporterExporterPrivate::warningInfoAboutInvalidFilter(const QStringList &emptyFilters) const
0109 {
0110     if (!emptyFilters.isEmpty()) {
0111         KMessageBox::informationList(mParent,
0112                                      i18n("The following filters have not been saved because they were invalid "
0113                                           "(e.g. containing no actions or no search rules)."),
0114                                      emptyFilters,
0115                                      QString(),
0116                                      QStringLiteral("ShowInvalidFilterWarning"));
0117     }
0118 }
0119 
0120 FilterImporterExporter::FilterImporterExporter(QWidget *parent)
0121     : d(new FilterImporterExporterPrivate(parent))
0122 {
0123 }
0124 
0125 FilterImporterExporter::~FilterImporterExporter() = default;
0126 
0127 QList<MailFilter *> FilterImporterExporter::importFilters(bool &canceled, FilterImporterExporter::FilterType type, const QString &filename)
0128 {
0129     QString fileName(filename);
0130 
0131     QFile file;
0132     if ((type != ThunderBirdFilter) && (type != IcedoveFilter) && (type != SeaMonkeyFilter)) {
0133         if (fileName.isEmpty()) {
0134             QString title;
0135             QString defaultPath;
0136             switch (type) {
0137             case KMailFilter:
0138                 title = i18n("Import KMail Filters");
0139                 defaultPath = QDir::homePath();
0140                 break;
0141             case ThunderBirdFilter:
0142             case IcedoveFilter:
0143             case SeaMonkeyFilter:
0144                 break;
0145             case EvolutionFilter:
0146                 title = i18n("Import Evolution Filters");
0147                 defaultPath = MailCommon::FilterImporterEvolution::defaultFiltersSettingsPath();
0148                 break;
0149             case SylpheedFilter:
0150                 title = i18n("Import Sylpheed Filters");
0151                 defaultPath = MailCommon::FilterImporterSylpheed::defaultFiltersSettingsPath();
0152                 break;
0153             case ProcmailFilter:
0154                 title = i18n("Import Procmail Filters");
0155                 defaultPath = MailCommon::FilterImporterProcmail::defaultFiltersSettingsPath();
0156                 break;
0157             case BalsaFilter:
0158                 title = i18n("Import Balsa Filters");
0159                 defaultPath = MailCommon::FilterImporterBalsa::defaultFiltersSettingsPath();
0160                 break;
0161             case ClawsMailFilter:
0162                 title = i18n("Import Claws Mail Filters");
0163                 defaultPath = MailCommon::FilterImporterClawsMails::defaultFiltersSettingsPath();
0164                 break;
0165             case GmailFilter:
0166                 title = i18n("Import Gmail Filters");
0167                 defaultPath = MailCommon::FilterImporterGmail::defaultFiltersSettingsPath();
0168                 break;
0169             }
0170 
0171             fileName = QFileDialog::getOpenFileName(d->mParent, title, defaultPath);
0172             if (fileName.isEmpty()) {
0173                 canceled = true;
0174                 return {}; // cancel
0175             }
0176         }
0177         file.setFileName(fileName);
0178         if (!file.open(QIODevice::ReadOnly)) {
0179             KMessageBox::error(d->mParent,
0180                                i18n("The selected file is not readable. "
0181                                     "Your file access permissions might be insufficient."));
0182             return {};
0183         }
0184     }
0185 
0186     QList<MailFilter *> imported;
0187     QStringList emptyFilter;
0188 
0189     switch (type) {
0190     case KMailFilter: {
0191         const KSharedConfig::Ptr config = KSharedConfig::openConfig(fileName);
0192         imported = readFiltersFromConfig(config, emptyFilter);
0193         break;
0194     }
0195     case IcedoveFilter:
0196     case SeaMonkeyFilter:
0197     case ThunderBirdFilter:
0198         if (fileName.isEmpty()) {
0199             QString defaultPath;
0200             if (type == ThunderBirdFilter) {
0201                 defaultPath = MailCommon::FilterImporterThunderbird::defaultThunderbirdFiltersSettingsPath();
0202             } else if (type == IcedoveFilter) {
0203                 defaultPath = MailCommon::FilterImporterThunderbird::defaultIcedoveFiltersSettingsPath();
0204             } else if (type == SeaMonkeyFilter) {
0205                 defaultPath = MailCommon::FilterImporterThunderbird::defaultSeaMonkeyFiltersSettingsPath();
0206             }
0207 
0208             QPointer<SelectThunderbirdFilterFilesDialog> selectThunderBirdFileDialog = new SelectThunderbirdFilterFilesDialog(defaultPath, d->mParent);
0209             selectThunderBirdFileDialog->setStartDir(QUrl::fromLocalFile(defaultPath));
0210             if (selectThunderBirdFileDialog->exec()) {
0211                 const QStringList lstFiles = selectThunderBirdFileDialog->selectedFiles();
0212                 for (const QString &url : lstFiles) {
0213                     QFile fileThunderbird(url);
0214                     if (!fileThunderbird.open(QIODevice::ReadOnly)) {
0215                         KMessageBox::error(d->mParent,
0216                                            i18n("The selected file is not readable. "
0217                                                 "Your file access permissions might be insufficient."));
0218                     } else {
0219                         auto thunderBirdFilter = new MailCommon::FilterImporterThunderbird(&fileThunderbird);
0220 
0221                         imported.append(thunderBirdFilter->importFilter());
0222                         emptyFilter.append(thunderBirdFilter->emptyFilter());
0223                         delete thunderBirdFilter;
0224                     }
0225                 }
0226             } else {
0227                 canceled = true;
0228                 delete selectThunderBirdFileDialog;
0229                 return {};
0230             }
0231             delete selectThunderBirdFileDialog;
0232         } else {
0233             file.setFileName(fileName);
0234             if (!file.open(QIODevice::ReadOnly)) {
0235                 KMessageBox::error(d->mParent,
0236                                    i18n("The selected file is not readable. "
0237                                         "Your file access permissions might be insufficient."));
0238                 return {};
0239             }
0240 
0241             auto thunderBirdFilter = new MailCommon::FilterImporterThunderbird(&file);
0242             imported = thunderBirdFilter->importFilter();
0243             emptyFilter = thunderBirdFilter->emptyFilter();
0244             delete thunderBirdFilter;
0245         }
0246         break;
0247     case EvolutionFilter: {
0248         auto filter = new MailCommon::FilterImporterEvolution(&file);
0249 
0250         imported = filter->importFilter();
0251         emptyFilter = filter->emptyFilter();
0252         delete filter;
0253         break;
0254     }
0255     case SylpheedFilter: {
0256         auto filter = new MailCommon::FilterImporterSylpheed(&file);
0257 
0258         imported = filter->importFilter();
0259         emptyFilter = filter->emptyFilter();
0260         delete filter;
0261         break;
0262     }
0263     case ProcmailFilter: {
0264         auto filter = new MailCommon::FilterImporterProcmail(&file);
0265 
0266         imported = filter->importFilter();
0267         emptyFilter = filter->emptyFilter();
0268         delete filter;
0269         break;
0270     }
0271     case BalsaFilter: {
0272         auto filter = new MailCommon::FilterImporterBalsa(&file);
0273 
0274         imported = filter->importFilter();
0275         emptyFilter = filter->emptyFilter();
0276         delete filter;
0277         break;
0278     }
0279     case ClawsMailFilter: {
0280         auto filter = new MailCommon::FilterImporterClawsMails(&file);
0281 
0282         imported = filter->importFilter();
0283         emptyFilter = filter->emptyFilter();
0284         delete filter;
0285         break;
0286     }
0287     case GmailFilter: {
0288         auto filter = new MailCommon::FilterImporterGmail(&file);
0289 
0290         imported = filter->importFilter();
0291         emptyFilter = filter->emptyFilter();
0292         delete filter;
0293         break;
0294     }
0295     }
0296     d->warningInfoAboutInvalidFilter(emptyFilter);
0297     file.close();
0298 
0299     QPointer<FilterSelectionDialog> dlg = new FilterSelectionDialog(d->mParent);
0300     dlg->setFilters(imported);
0301     if (dlg->exec() == QDialog::Accepted) {
0302         const QList<MailFilter *> selected = dlg->selectedFilters();
0303         delete dlg;
0304         return selected;
0305     }
0306     delete dlg;
0307     canceled = true;
0308     return {};
0309 }
0310 
0311 void FilterImporterExporter::exportFilters(const QList<MailFilter *> &filters, const QUrl &fileName, bool saveAll)
0312 {
0313     QUrl saveUrl;
0314     if (fileName.isEmpty()) {
0315         saveUrl = QFileDialog::getSaveFileUrl(d->mParent,
0316                                               i18n("Export Filters"),
0317                                               QUrl::fromLocalFile(QDir::homePath()),
0318                                               QString(),
0319                                               nullptr,
0320                                               QFileDialog::DontConfirmOverwrite);
0321 
0322         if (saveUrl.isEmpty() || !MessageViewer::Util::checkOverwrite(saveUrl, d->mParent)) {
0323             qDeleteAll(filters);
0324             return;
0325         }
0326     } else {
0327         saveUrl = fileName;
0328     }
0329     KSharedConfig::Ptr config = KSharedConfig::openConfig(saveUrl.toLocalFile());
0330     if (saveAll) {
0331         writeFiltersToConfig(filters, config, true);
0332         // qDeleteAll(filters);
0333     } else {
0334         std::unique_ptr<FilterSelectionDialog> dlg(new FilterSelectionDialog(d->mParent));
0335         dlg->setFilters(filters);
0336         if (dlg->exec() == QDialog::Accepted && dlg) {
0337             QList<MailFilter *> lst = dlg->selectedFilters();
0338             writeFiltersToConfig(lst, config, true);
0339             qDeleteAll(lst);
0340         }
0341     }
0342 }