MailImporter

filterbalsa.cpp
1/*
2 SPDX-FileCopyrightText: 2012-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "filterbalsa.h"
8
9#include <KLocalizedString>
10#include <QFileDialog>
11
12using namespace MailImporter;
13
14class MailImporter::FilterBalsaPrivate
15{
16public:
17 int mImportDirDone = 0;
18 int mTotalDir = 0;
19};
20/** Default constructor. */
22 : Filter(i18n("Import Balsa Local Mails and Folder Structure"),
23 QStringLiteral("Laurent Montel"),
24 i18n("<p><b>Balsa import filter</b></p>"
25 "<p>Select the base directory of your local Balsa mailfolder (usually ~/mail/).</p>"
26 "<p>Since it is possible to recreate the folder structure, the folders "
27 "will be stored under: \"Balsa-Import\".</p>"))
28 , d(new MailImporter::FilterBalsaPrivate)
29{
30}
31
32/** Destructor. */
34
35QString FilterBalsa::isMailerFound()
36{
37 QDir directory(FilterBalsa::defaultSettingsPath());
38 if (directory.exists()) {
39 return i18nc("name of balsa application", "Balsa");
40 }
41 return {};
42}
43
44QString FilterBalsa::defaultSettingsPath()
45{
46 return QDir::homePath() + QLatin1StringView("/.balsa/");
47}
48
49QString FilterBalsa::localMailDirPath()
50{
51 return QDir::homePath() + QLatin1StringView("/mail/");
52}
53
54/** Recursive import of KMail maildir. */
56{
57 clearCountDuplicate();
58 QString balsaDir = localMailDirPath();
59 QDir d(balsaDir);
60 if (!d.exists()) {
61 balsaDir = QDir::homePath();
62 }
63 // Select directory from where I have to import files
64 const QString maildir = QFileDialog::getExistingDirectory(nullptr, QString(), balsaDir);
65 importMails(maildir);
66}
67
68void FilterBalsa::processDirectory(const QString &path)
69{
70 QDir dir(path);
71 const QStringList rootSubDirs = dir.entryList(QStringList(QStringLiteral("*")), QDir::Dirs | QDir::Hidden, QDir::Name);
72 QStringList::ConstIterator end = rootSubDirs.constEnd();
73 for (QStringList::ConstIterator filename = rootSubDirs.constBegin(); filename != end; ++filename) {
74 if (filterInfo()->shouldTerminate()) {
75 break;
76 }
77 if (!(*filename == QLatin1Char('.') || *filename == QLatin1StringView(".."))) {
78 filterInfo()->setCurrent(0);
79 importDirContents(dir.filePath(*filename));
80 filterInfo()->setOverall((d->mTotalDir > 0) ? (int)((float)d->mImportDirDone / d->mTotalDir * 100) : 0);
81 filterInfo()->setCurrent(100);
82 }
83 }
84}
85
87{
88 if (maildir.isEmpty()) {
89 filterInfo()->alert(i18n("No directory selected."));
90 return;
91 }
92 setMailDir(maildir);
93 /**
94 * If the user only select homedir no import needed because
95 * there should be no files and we surely import wrong files.
96 */
97 if (mailDir() == QDir::homePath() || mailDir() == (QDir::homePath() + QLatin1Char('/'))) {
98 filterInfo()->addErrorLogEntry(i18n("No files found for import."));
99 } else {
100 filterInfo()->setOverall(0);
101 d->mImportDirDone = 0;
102
103 /** Recursive import of the MailArchives */
104 QDir dir(mailDir());
105 d->mTotalDir = Filter::countDirectory(dir, true /*search hidden directory*/);
106
107 processDirectory(mailDir());
108
109 filterInfo()->addInfoLogEntry(i18n("Finished importing emails from %1", mailDir()));
110
111 if (countDuplicates() > 0) {
112 filterInfo()->addInfoLogEntry(i18np("1 duplicate message not imported", "%1 duplicate messages not imported", countDuplicates()));
113 }
114
115 if (filterInfo()->shouldTerminate()) {
116 filterInfo()->addInfoLogEntry(i18n("Finished import, canceled by user."));
117 }
118 }
119 filterInfo()->setCurrent(100);
120 filterInfo()->setOverall(100);
121}
122
123/**
124 * Import of a directory contents.
125 * @param info Information storage for the operation.
126 * @param dirName The name of the directory to import.
127 */
128void FilterBalsa::importDirContents(const QString &dirName)
129{
130 /** Here Import all archives in the current dir */
131 importFiles(dirName);
132
133 /** If there are subfolders, we import them one by one */
134 processDirectory(dirName);
135}
136
137/**
138 * Import the files within a Folder.
139 * @param info Information storage for the operation.
140 * @param dirName The name of the directory to import.
141 */
142void FilterBalsa::importFiles(const QString &dirName)
143{
144 QDir dir(dirName);
145 QString _path;
146 bool generatedPath = false;
147
148 QDir importDir(dirName);
149 const QStringList files = importDir.entryList(QStringList(QStringLiteral("[^\\.]*")), QDir::Files, QDir::Name);
150 int currentFile = 1;
151 int numFiles = files.size();
152 QStringList::ConstIterator filesEnd(files.constEnd());
153
154 for (QStringList::ConstIterator mailFile = files.constBegin(); mailFile != filesEnd; ++mailFile, ++currentFile) {
155 if (filterInfo()->shouldTerminate()) {
156 return;
157 }
158 QString temp_mailfile = *mailFile;
159 if (!(temp_mailfile.endsWith(QLatin1StringView(".db")) || temp_mailfile.endsWith(QLatin1StringView(".cmeta"))
160 || temp_mailfile.endsWith(QLatin1StringView(".ev-summary")) || temp_mailfile.endsWith(QLatin1StringView(".ibex.index"))
161 || temp_mailfile.endsWith(QLatin1StringView(".ibex.index.data")))) {
162 if (!generatedPath) {
163 _path = i18nc("define folder name where we import evolution mails", "Evolution-Import");
164 QString _tmp = dir.filePath(*mailFile);
165 _tmp.remove(mailDir(), Qt::CaseSensitive);
166 QStringList subFList = _tmp.split(QLatin1Char('/'), Qt::SkipEmptyParts);
168 for (QStringList::ConstIterator it = subFList.constBegin(); it != end; ++it) {
169 QString _cat = *it;
170 if (!(_cat == *mailFile)) {
171 if (_cat.startsWith(QLatin1Char('.'))) {
172 _cat.remove(0, 1);
173 }
174 // Evolution store inbox as "."
175 if (_cat.startsWith(QLatin1Char('.'))) {
176 _cat.replace(0, 1, QStringLiteral("Inbox/"));
177 }
178
179 _path += QLatin1Char('/') + _cat;
180 _path.replace(QLatin1Char('.'), QLatin1Char('/'));
181 }
182 }
183 if (_path.endsWith(QLatin1StringView("cur"))) {
184 _path.remove(_path.length() - 4, 4);
185 }
186 QString _info = _path;
187 filterInfo()->addInfoLogEntry(i18n("Import folder %1...", _info));
188 filterInfo()->setFrom(_info);
189 filterInfo()->setTo(_path);
190 generatedPath = true;
191 }
192 const MailImporter::MessageStatus status = statusFromFile(*mailFile);
193
194 if (!importMessage(_path, dir.filePath(*mailFile), filterInfo()->removeDupMessage(), status)) {
195 filterInfo()->addErrorLogEntry(i18n("Could not import %1", *mailFile));
196 }
197 filterInfo()->setCurrent((int)((float)currentFile / numFiles * 100));
198 }
199 }
200}
201
202MailImporter::MessageStatus FilterBalsa::statusFromFile(const QString &filename)
203{
205 const int statusIndex = filename.indexOf(QLatin1StringView(":2,"));
206 if (statusIndex != -1) {
207 const QString statusStr = filename.right(filename.length() - statusIndex - 3);
208 if (statusStr.contains(QLatin1Char('S'))) {
209 status.setRead(true);
210 }
211 if (statusStr.contains(QLatin1Char('F'))) {
212 // TODO
213 }
214 if (statusStr.contains(QLatin1Char('R'))) {
215 status.setReplied(true);
216 }
217 if (statusStr.contains(QLatin1Char('P'))) {
218 status.setForwarded(true);
219 }
220 }
221 return status;
222}
~FilterBalsa() override
Destructor.
void import() override
Recursive import of KMail maildir.
FilterBalsa()
Default constructor.
void importMails(const QString &maildir)
The Filter class.
Definition filters.h:29
The MessageStatus class.
Q_SCRIPTABLE CaptureState status()
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
KIOCORE_EXPORT QString dir(const QString &fileClass)
bool exists() const const
QString homePath()
QString getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, Options options)
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator end()
qsizetype size() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
CaseSensitive
SkipEmptyParts
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:08:31 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.