1 /***************************************************************************
2 FilterPMail.cxx - Pegasus-Mail import
5 copyright : (C) 2001 by Holger Schurig <holgerschurig@gmx.de>
6 (C) 2005 by Danny Kukawka <danny.kukawka@web.de>
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
18 #include <KLocalizedString>
19 #include <kfiledialog.h>
22 #include <QTemporaryFile>
25 #include "filter_pmail.h"
27 using namespace MailImporter
;
29 FilterPMail::FilterPMail() :
30 Filter(i18n("Import Folders From Pegasus-Mail"),
31 i18n("Holger Schurig <br>( rewritten by Danny Kukawka )"),
32 i18n("<p>Select the Pegasus-Mail directory on your system (containing *.CNM, *.PMM and *.MBX files). "
33 "On many systems this is stored in C:\\pmail\\mail or C:\\pmail\\mail\\admin</p>"
34 "<p><b>Note:</b> Since it is possible to recreate the folder structure, the folders "
35 "will be stored under: \"PegasusMail-Import\".</p>"))
38 FilterPMail::~FilterPMail()
42 void FilterPMail::import()
44 // Select directory from where I have to import files
45 QPointer
<KFileDialog
> kfd
= new KFileDialog(QDir::homePath(), QString(), 0);
46 kfd
->setMode(KFile::Directory
| KFile::LocalOnly
);
48 const QString maildir
= kfd
->selectedFile();
55 void FilterPMail::importMails(const QString
&chosenDir
)
57 setMailDir(chosenDir
);
58 if (mailDir().isEmpty()) {
59 filterInfo()->alert(i18n("No directory selected."));
63 // Count total number of files to be processed
64 filterInfo()->addInfoLogEntry(i18n("Counting files..."));
65 dir
.setPath(mailDir());
66 const QStringList files
= dir
.entryList(QStringList() << QLatin1String("*.[cC][nN][mM]") << QLatin1String("*.[pP][mM][mM]") << QLatin1String("*.[mM][bB][xX]"), QDir::Files
, QDir::Name
);
67 totalFiles
= files
.count();
69 qDebug() << "Count is" << totalFiles
;
71 if (!(folderParsed
= parseFolderMatrix(mailDir()))) {
72 filterInfo()->addErrorLogEntry(i18n("Cannot parse the folder structure; continuing import without subfolder support."));
75 filterInfo()->addInfoLogEntry(i18n("Importing new mail files ('.cnm')..."));
76 processFiles(QLatin1String("*.[cC][nN][mM]"), &FilterPMail::importNewMessage
);
77 filterInfo()->addInfoLogEntry(i18n("Importing mail folders ('.pmm')..."));
78 processFiles(QLatin1String("*.[pP][mM][mM]"), &FilterPMail::importMailFolder
);
79 filterInfo()->addInfoLogEntry(i18n("Importing 'UNIX' mail folders ('.mbx')..."));
80 processFiles(QLatin1String("*.[mM][bB][xX]"), &FilterPMail::importUnixMailFolder
);
82 filterInfo()->addInfoLogEntry(i18n("Finished importing emails from %1", mailDir()));
83 filterInfo()->setCurrent(100);
84 filterInfo()->setOverall(100);
87 /** this looks for all files with the filemask 'mask' and calls the 'workFunc' on each of them */
88 void FilterPMail::processFiles(const QString
&mask
, void(FilterPMail::* workFunc
)(const QString
&))
90 if (filterInfo()->shouldTerminate()) {
94 const QStringList files
= dir
.entryList(QStringList(mask
), QDir::Files
, QDir::Name
);
95 //qDebug() <<"Mask is" << mask <<" count is" << files.count();
96 QStringList::ConstIterator end
= files
.constEnd();
97 for (QStringList::ConstIterator mailFile
= files
.constBegin(); mailFile
!= end
; ++mailFile
) {
98 // Notify current file
99 QFileInfo
mailfilem_filterInfoo(*mailFile
);
100 filterInfo()->setFrom(mailfilem_filterInfoo
.fileName());
102 // Clear the other fields
103 filterInfo()->setTo(QString());
104 filterInfo()->setCurrent(QString());
105 filterInfo()->setCurrent(-1);
107 // call worker function, increase progressbar
108 (this->*workFunc
)(dir
.filePath(*mailFile
));
110 filterInfo()->setOverall((int)((float) currentFile
/ totalFiles
* 100));
111 filterInfo()->setCurrent(100);
112 if (filterInfo()->shouldTerminate()) {
118 /** this function imports one *.CNM message */
119 void FilterPMail::importNewMessage(const QString
&file
)
121 QString
destFolder(QLatin1String("PegasusMail-Import/New Messages"));
122 filterInfo()->setTo(destFolder
);
124 /* comment by Danny Kukawka:
125 * addMessage() == old function, need more time and check for duplicates
126 * addMessage_fastImport == new function, faster and no check for duplicates
128 if (filterInfo()->removeDupMessage()) {
129 addMessage(destFolder
, file
);
131 addMessage_fastImport(destFolder
, file
);
135 /** this function imports one mail folder file (*.PMM) */
136 void FilterPMail::importMailFolder(const QString
&file
)
138 // Format of a PMM file:
139 // First comes a header with 128 bytes. At the beginning is the name of
140 // the folder. Then there are some unknown bytes (strings). At offset 128
141 // the first message starts.
143 // Each message is terminated by a 0x1A byte. The next message follows
146 // The last message is followed by a 0x1A, too.
148 // 000000 6d 61 69 6c 73 65 72 76 65 72 2d 70 72 6f 6a 65 mailserver-proje
149 // 000010 63 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ct..............
150 // 000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
151 // 000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
152 // 000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
153 // 000050 00 00 00 00 00 00 36 30 34 37 35 37 32 45 3a 36 ......6047572E:6
154 // 000060 46 34 39 3a 46 4f 4c 30 31 33 35 35 00 00 00 00 F49:FOL01355....
155 // 000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
156 // 000080 52 65 74 75 72 6e 2d 50 61 74 68 3a 20 3c 75 72 Return-Path: <ur
158 // 000cb0 2d 2d 2d 2d 2d 2d 2d 2d 2d 2b 0d 0a 1a 52 65 74 ---------+...Ret
159 // 000cc0 75 72 6e 2d 50 61 74 68 3a 20 3c 62 6f 75 6e 63 urn-Path: <bounc
161 // 04dc50 46 30 33 38 44 2e 36 31 35 44 37 34 44 30 2d 2d F038D.615D74D0--
171 if (!f
.open(QIODevice::ReadOnly
)) {
172 filterInfo()->alert(i18n("Unable to open %1, skipping", file
));
175 l
= f
.read((char *) &pmm_head
, sizeof(pmm_head
));
176 QString
folder(i18nc("define folder name when we will import pegasus mail", "PegasusMail-Import") + QLatin1Char('/'));
178 folder
.append(getFolderName(QString::fromLatin1(pmm_head
.id
)));
180 folder
.append(QString::fromLatin1(pmm_head
.folder
));
182 filterInfo()->setTo(folder
);
183 filterInfo()->addInfoLogEntry(i18n("Importing %1", QString::fromLatin1("../") + QString::fromLatin1(pmm_head
.folder
)));
185 QByteArray
input(MAX_LINE
, '\0');
186 bool first_msg
= true;
189 QTemporaryFile tempfile
;
191 filterInfo()->setCurrent((int)(((float) f
.pos() / f
.size()) * 100));
194 // set the filepos back to last line minus the separate char (0x1a)
195 f
.seek(f
.pos() - l
+ 1);
198 // no problem to loose the last line in file. This only contains a separate char
199 while (! f
.atEnd() && (l
= f
.readLine(input
.data(), MAX_LINE
))) {
200 if (filterInfo()->shouldTerminate()) {
203 if (input
.at(0) == 0x1a) {
206 tempfile
.write(input
, l
);
211 if (filterInfo()->removeDupMessage()) {
212 addMessage(folder
, tempfile
.fileName());
214 addMessage_fastImport(folder
, tempfile
.fileName());
223 /** imports a 'unix' format mail folder (*.MBX) */
224 void FilterPMail::importUnixMailFolder(const QString
&file
)
232 QString
folder(QLatin1String("PegasusMail-Import/")), s(file
), separate
;
233 QByteArray
line(MAX_LINE
, '\0');
236 /** Get the folder name */
237 s
.replace(QRegExp(QLatin1String("mbx$")), QLatin1String("pmg"));
238 s
.replace(QRegExp(QLatin1String("MBX$")), QLatin1String("PMG"));
240 if (! f
.open(QIODevice::ReadOnly
)) {
241 filterInfo()->alert(i18n("Unable to open %1, skipping", s
));
244 f
.read((char *) &pmg_head
, sizeof(pmg_head
));
248 folder
.append(getFolderName(QString::fromLatin1(pmg_head
.id
)));
250 folder
.append(QString::fromLatin1(pmg_head
.folder
));
253 filterInfo()->setTo(folder
);
254 filterInfo()->setTo(folder
);
257 /** Read in the mbox */
259 if (! f
.open(QIODevice::ReadOnly
)) {
260 filterInfo()->alert(i18n("Unable to open %1, skipping", s
));
262 filterInfo()->addInfoLogEntry(i18n("Importing %1", QLatin1String("../") + QString::fromLatin1(pmg_head
.folder
)));
263 l
= f
.readLine(line
.data(), MAX_LINE
); // read the first line which is unneeded
264 while (! f
.atEnd()) {
265 QTemporaryFile tempfile
;
268 // we lost the last line, which is the first line of the new message in
269 // this lopp, but this is ok, because this is the separate line with
270 // "From ???@???" and we can forget them
271 while (! f
.atEnd() && (l
= f
.readLine(line
.data(), MAX_LINE
)) && ((separate
= QString::fromLatin1(line
.data())).left(5) != QLatin1String("From "))) {
272 tempfile
.write(line
.data(), l
);
273 if (filterInfo()->shouldTerminate()) {
278 if (filterInfo()->removeDupMessage()) {
279 addMessage(folder
, tempfile
.fileName());
281 addMessage_fastImport(folder
, tempfile
.fileName());
285 filterInfo()->setCurrent(i18n("Message %1", n
));
286 filterInfo()->setCurrent((int)(((float) f
.pos() / f
.size()) * 100));
292 /** Parse the m_filterInfoormation about folderstructure to folderMatrix */
293 bool FilterPMail::parseFolderMatrix(const QString
&chosendir
)
295 qDebug() << "Start parsing the foldermatrix.";
296 filterInfo()->addInfoLogEntry(i18n("Parsing the folder structure..."));
298 QFile
hierarch(chosendir
+ QLatin1String("/hierarch.pm"));
299 if (! hierarch
.open(QIODevice::ReadOnly
)) {
300 filterInfo()->alert(i18n("Unable to open %1, skipping", chosendir
+ QLatin1String("hierarch.pm")));
305 while (!hierarch
.atEnd()) {
306 tmpRead
= hierarch
.readLine();
307 if (tmpRead
.isEmpty()) {
311 tmpRead
.remove(tmpRead
.length() - 2, 2);
312 QStringList tmpList
= QString::fromLatin1(tmpRead
).split(QLatin1Char(','), QString::SkipEmptyParts
);
314 QStringList::ConstIterator
end(tmpList
.constEnd());
315 for (QStringList::ConstIterator it
= tmpList
.constBegin(); it
!= end
; ++it
, ++i
) {
318 tmpArray
[i
] = _tmp
.remove(QLatin1Char('\"'));
324 folderMatrix
.append(FolderStructure(tmpArray
));
331 /** get the foldername for a given file ID from folderMatrix */
332 QString
FilterPMail::getFolderName(const QString
&ID
)
339 FolderStructureIterator
end(folderMatrix
.end());
340 for (FolderStructureIterator it
= folderMatrix
.begin(); it
!= end
; ++it
) {
341 FolderStructure tmp
= *it
;
343 QString _ID
= tmp
[2];
345 QString _type
= tmp
[0] + tmp
[1];
346 if ((_type
== QLatin1String("21"))) {
350 folder
.prepend((tmp
[4] + QLatin1Char('/')));