File indexing completed on 2024-11-10 04:50:07
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 }