1 /*
2 SPDX-FileCopyrightText: 2012-2021 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
12 using namespace MailImporter;
13
14 class MailImporter::FilterBalsaPrivate
15 {
16 public:
17 int mImportDirDone = 0;
18 int mTotalDir = 0;
19 };
20 /** Default constructor. */
FilterBalsa()21 FilterBalsa::FilterBalsa()
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. */
33 FilterBalsa::~FilterBalsa() = default;
34
isMailerFound()35 QString 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
defaultSettingsPath()44 QString FilterBalsa::defaultSettingsPath()
45 {
46 return QDir::homePath() + QLatin1String("/.balsa/");
47 }
48
localMailDirPath()49 QString FilterBalsa::localMailDirPath()
50 {
51 return QDir::homePath() + QLatin1String("/mail/");
52 }
53
54 /** Recursive import of KMail maildir. */
import()55 void FilterBalsa::import()
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
processDirectory(const QString & path)68 void 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 == QLatin1String(".."))) {
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
importMails(const QString & maildir)86 void FilterBalsa::importMails(const QString &maildir)
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 */
importDirContents(const QString & dirName)128 void 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 */
importFiles(const QString & dirName)142 void 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(QLatin1String(".db")) || temp_mailfile.endsWith(QLatin1String(".cmeta"))
160 || temp_mailfile.endsWith(QLatin1String(".ev-summary")) || temp_mailfile.endsWith(QLatin1String(".ibex.index"))
161 || temp_mailfile.endsWith(QLatin1String(".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);
167 QStringList::ConstIterator end(subFList.end());
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(QLatin1String("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
statusFromFile(const QString & filename)202 MailImporter::MessageStatus FilterBalsa::statusFromFile(const QString &filename)
203 {
204 MailImporter::MessageStatus status;
205 const int statusIndex = filename.indexOf(QLatin1String(":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 }
223