MailImporter

filterthebat.cpp
1/*
2 filterthebat.cpp - TheBat! mail import
3
4 SPDX-FileCopyrightText: 2005 Danny Kukawka <danny.kukawka@web.de>
5 SPDX-FileCopyrightText: 2012-2024 Laurent Montel <montel@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "filterthebat.h"
11
12#include <KLocalizedString>
13#include <QFileDialog>
14#include <QRegularExpression>
15#include <QTemporaryFile>
16
17using namespace MailImporter;
18
19class MailImporter::FilterTheBatPrivate
20{
21public:
22 int mImportDirDone = 0;
23 int mTotalDir = 0;
24};
25
26/** Default constructor. */
28 : Filter(i18n("Import The Bat! Mails and Folder Structure"),
29 QStringLiteral("Danny Kukawka"),
30 i18n("<p><b>The Bat! import filter</b></p>"
31 "<p>Select the base directory of the \'The Bat!\' local mailfolder you "
32 "want to import.</p>"
33 "<p><b>Note:</b> This filter imports the *.tbb-files from \'The Bat!\' "
34 "local folder, e.g. from POP accounts, and not from IMAP/DIMAP accounts.</p>"
35 "<p>Since it is possible to recreate the folder structure, the folders "
36 "will be stored under: \"TheBat-Import\" in your local account.</p>"))
37 , d(new MailImporter::FilterTheBatPrivate)
38{
39}
40
41/** Destructor. */
43
44/** Recursive import of The Bat! maildir. */
46{
47 const QString _homeDir = QDir::homePath();
48 // Select directory from where I have to import files
49 const QString maildir = QFileDialog::getExistingDirectory(nullptr, QString(), _homeDir);
50 if (!maildir.isEmpty()) {
51 importMails(maildir);
52 }
53}
54
55void FilterTheBat::processDirectory(const QString &path)
56{
57 QDir dir(path);
58 const QStringList rootSubDirs = dir.entryList(QStringList(QStringLiteral("[^\\.]*")), QDir::Dirs, QDir::Name);
59 QStringList::ConstIterator end = rootSubDirs.constEnd();
60 for (QStringList::ConstIterator filename = rootSubDirs.constBegin(); filename != end; ++filename) {
61 if (filterInfo()->shouldTerminate()) {
62 break;
63 }
64 importDirContents(dir.filePath(*filename));
65 filterInfo()->setOverall((d->mTotalDir > 0) ? (int)((float)d->mImportDirDone / d->mTotalDir * 100) : 0);
66 ++d->mImportDirDone;
67 }
68}
69
71{
72 if (maildir.isEmpty()) {
73 filterInfo()->alert(i18n("No directory selected."));
74 return;
75 }
76 setMailDir(maildir);
77 /**
78 * If the user only select homedir no import needed because
79 * there should be no files and we surely import wrong files.
80 */
81 if (mailDir() == QDir::homePath() || mailDir() == (QDir::homePath() + QLatin1Char('/'))) {
82 filterInfo()->addErrorLogEntry(i18n("No files found for import."));
83 } else {
84 filterInfo()->setOverall(0);
85 d->mImportDirDone = 0;
86
87 /** Recursive import of the MailFolders */
88 QDir dir(mailDir());
89 d->mTotalDir = Filter::countDirectory(dir, false);
90
91 processDirectory(mailDir());
92
93 filterInfo()->addInfoLogEntry(i18n("Finished importing emails from %1", mailDir()));
94 if (countDuplicates() > 0) {
95 filterInfo()->addInfoLogEntry(i18np("1 duplicate message not imported", "%1 duplicate messages not imported", countDuplicates()));
96 }
97 }
98 if (filterInfo()->shouldTerminate()) {
99 filterInfo()->addInfoLogEntry(i18n("Finished import, canceled by user."));
100 }
101
102 clearCountDuplicate();
103 filterInfo()->setCurrent(100);
104 filterInfo()->setOverall(100);
105}
106
107/**
108 * Import of a directory contents.
109 * @param info Information storage for the operation.
110 * @param dirName The name of the directory to import.
111 */
112void FilterTheBat::importDirContents(const QString &dirName)
113{
114 if (filterInfo()->shouldTerminate()) {
115 return;
116 }
117
118 /** Here Import all archives in the current dir */
119 QDir importDir(dirName);
120 const QStringList files = importDir.entryList(QStringList(QStringLiteral("*.[tT][bB][bB]")), QDir::Files, QDir::Name);
122 for (QStringList::ConstIterator mailFile = files.constBegin(); mailFile != end; ++mailFile) {
123 QString temp_mailfile = *mailFile;
124 importFiles((dirName + QLatin1Char('/') + temp_mailfile));
125 if (filterInfo()->shouldTerminate()) {
126 return;
127 }
128 }
129
130 /** If there are subfolders, we import them one by one */
131 processDirectory(dirName);
132}
133
134/**
135 * Import the files within a Folder.
136 * @param info Information storage for the operation.
137 * @param dirName The name of the directory to import.
138 */
139void FilterTheBat::importFiles(const QString &FileName)
140{
141 // Format of a tbb-file from The Bat! 3.x
142 // ----------------------------------------
143 // First comes a header of 3K (3128 byte/ 0x00000c38), which we can forget.
144 // The byte 3129 is the first character of the first message.
145 //
146 // The end of a message is marked through "! p 0" and 43 following characters.
147 // (within: "_UB", blanks and some other chars.) Together are 48 characters as
148 // separator.
149 // ----------------------------------------
150
151 long l = 0;
152 QByteArray input(50, '\0');
153 const QRegularExpression regexp(QStringLiteral("!.p.0"));
154 QFile tbb(FileName);
155 int iFound = 0;
156 int count = 0;
157 long endOfEmail = 0;
158 QList<long> offsets;
159
160 if (!tbb.open(QIODevice::ReadOnly)) {
161 filterInfo()->alert(i18n("Unable to open %1, skipping", FileName));
162 } else {
163 // BUILD the index of messages :
164 // We need this really ugly way, because read with tbb.readLine()
165 // does not work correct. Maybe in come in a continuous loop !!!
166 // Reason:
167 // if you use readLine() to read from a file with binary data
168 // QFile::at() and QFile::atEnd() return wrong value. So we
169 // never get QFile::atEnd() == true in some cases. This looks
170 // like a bug in Qt3 maybe fixed in Qt4.
171 //
172 while ((l = tbb.read(input.data(), 50))) {
173 if (filterInfo()->shouldTerminate()) {
174 tbb.close();
175 return;
176 }
177 QString _tmp = QString::fromUtf8(input.data());
178
179 if (tbb.atEnd()) {
180 break;
181 }
182
183 iFound = _tmp.count(regexp);
184 if (!iFound) {
185 iFound = _tmp.lastIndexOf(QLatin1Char('!'));
186 if (iFound >= 0 && ((l - iFound) < 5)) {
187 int _i = tbb.pos();
188 tbb.seek((_i - iFound));
189 }
190 } else {
191 ++count;
192 endOfEmail = (tbb.pos() - l + _tmp.indexOf(regexp));
193 offsets.append(endOfEmail);
194 }
195 }
196
197 // IMPORT the messages:
198 if (!offsets.empty() || (offsets.empty() && (tbb.size() > 3128))) {
199 offsets.append(tbb.size());
200 tbb.seek(3128);
201 long lastPos = 3128;
202 long endPos = 0;
203
204 QString _path = i18nc("Define folder where we will import thebat mails", "TheBat-Import") + QLatin1Char('/');
205 QString _tmp = FileName;
206 _tmp.remove(_tmp.length() - 13, 13);
207 _path += _tmp.remove(mailDir(), Qt::CaseSensitive);
208 QString _info = _path;
209 filterInfo()->addInfoLogEntry(i18n("Import folder %1...", _info.remove(0, 14)));
210 filterInfo()->setTo(_path);
211 filterInfo()->setFrom(QLatin1StringView("../") + _info + QLatin1StringView("/messages.tbb"));
212
213 QList<long>::Iterator end = offsets.end();
214 for (QList<long>::Iterator it = offsets.begin(); it != end; ++it) {
215 if (filterInfo()->shouldTerminate()) {
216 tbb.close();
217 return;
218 }
219 endPos = *it;
220 QByteArray input(endPos - lastPos, '\0');
221 tbb.read(input.data(), endPos - lastPos);
222
223 QTemporaryFile tmp;
224 tmp.open();
225 tmp.write(input.constData(), endPos - lastPos);
226 tmp.flush();
227
228 if (!importMessage(_path, tmp.fileName(), filterInfo()->removeDupMessage())) {
229 filterInfo()->addErrorLogEntry(i18n("Could not import %1", tmp.fileName()));
230 }
231
232 lastPos = endPos + 48;
233 tbb.seek(lastPos);
234 filterInfo()->setCurrent((int)(((float)tbb.pos() / tbb.size()) * 100));
235 }
236 }
237 }
238 tbb.close();
239}
FilterTheBat()
Default constructor.
void importMails(const QString &maildir)
void import() override
Recursive import of The Bat! maildir.
~FilterTheBat() override
Destructor.
The Filter class.
Definition filters.h:29
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...)
const QList< QKeySequence > & end()
QString homePath()
bool flush()
QString getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, Options options)
qint64 write(const QByteArray &data)
typedef ConstIterator
void append(QList< T > &&value)
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
bool empty() const const
iterator end()
qsizetype count() const const
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
CaseSensitive
virtual QString fileName() const const override
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:39 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.