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 ***************************************************************************/
19 #include <kfiledialog.h>
21 #include <ktemporaryfile.h>
24 #include "filter_pmail.hxx"
27 FilterPMail::FilterPMail() :
28 Filter(i18n("Import Folders From Pegasus-Mail"),
29 "Holger Schurig <br>( rewritten by Danny Kukawka )",
30 i18n("<p>Select the Pegasus-Mail directory on your system (containing *.CNM, *.PMM and *.MBX files). "
31 "On many systems this is stored in C:\\pmail\\mail or C:\\pmail\\mail\\admin</p>"
32 "<p><b>Note:</b> Since it is possible to recreate the folder structure, the folders "
33 "will be stored under: \"PegasusMail-Import\".</p>"))
36 FilterPMail::~FilterPMail()
40 void FilterPMail::import(FilterInfo
*info
)
44 // Select directory from where I have to import files
46 kfd
= new KFileDialog( QDir::homePath(), "", 0 );
47 kfd
->setMode(KFile::Directory
| KFile::LocalOnly
);
49 chosenDir
= kfd
->selectedFile();
51 if (chosenDir
.isEmpty()) {
52 info
->alert(i18n("No directory selected."));
56 // Count total number of files to be processed
57 info
->addLog(i18n("Counting files..."));
58 dir
.setPath (chosenDir
);
59 const QStringList files
= dir
.entryList(QStringList("*.[cC][nN][mM]")<<"*.[pP][mM][mM]"<<"*.[mM][bB][xX]", QDir::Files
, QDir::Name
);
60 totalFiles
= files
.count();
62 kDebug() <<"Count is" << totalFiles
;
64 if(!(folderParsed
= parseFolderMatrix())) {
65 info
->addLog(i18n("Cannot parse the folder structure; continuing import without subfolder support."));
68 info
->addLog(i18n("Importing new mail files ('.cnm')..."));
69 processFiles("*.[cC][nN][mM]", &FilterPMail::importNewMessage
);
70 info
->addLog(i18n("Importing mail folders ('.pmm')..."));
71 processFiles("*.[pP][mM][mM]", &FilterPMail::importMailFolder
);
72 info
->addLog(i18n("Importing 'UNIX' mail folders ('.mbx')..."));
73 processFiles("*.[mM][bB][xX]", &FilterPMail::importUnixMailFolder
);
75 info
->addLog( i18n("Finished importing emails from %1", chosenDir
));
76 info
->setCurrent(100);
77 info
->setOverall(100);
81 /** this looks for all files with the filemask 'mask' and calls the 'workFunc' on each of them */
82 void FilterPMail::processFiles(const QString
& mask
, void(FilterPMail::* workFunc
)(const QString
&) )
84 if (inf
->shouldTerminate()) return;
86 const QStringList files
= dir
.entryList(QStringList(mask
), QDir::Files
, QDir::Name
);
87 //kDebug() <<"Mask is" << mask <<" count is" << files.count();
88 for ( QStringList::ConstIterator mailFile
= files
.constBegin(); mailFile
!= files
.constEnd(); ++mailFile
) {
89 // Notify current file
90 QFileInfo
mailfileinfo(*mailFile
);
91 inf
->setFrom(mailfileinfo
.fileName());
93 // Clear the other fields
94 inf
->setTo(QString());
95 inf
->setCurrent(QString());
98 // call worker function, increase progressbar
99 (this->*workFunc
)(dir
.filePath(*mailFile
));
101 inf
->setOverall( (int) ((float) currentFile
/ totalFiles
* 100));
102 inf
->setCurrent( 100 );
103 if (inf
->shouldTerminate()) return;
108 /** this function imports one *.CNM message */
109 void FilterPMail::importNewMessage(const QString
& file
)
111 QString
destFolder("PegasusMail-Import/New Messages");
112 inf
->setTo(destFolder
);
114 /* comment by Danny Kukawka:
115 * addMessage() == old function, need more time and check for duplicates
116 * addMessage_fastImport == new function, faster and no check for duplicates
118 if(inf
->removeDupMsg
)
119 addMessage( inf
, destFolder
, file
);
121 addMessage_fastImport( inf
, destFolder
, file
);
125 /** this function imports one mail folder file (*.PMM) */
126 void FilterPMail::importMailFolder(const QString
& file
)
128 // Format of a PMM file:
129 // First comes a header with 128 bytes. At the beginning is the name of
130 // the folder. Then there are some unknown bytes (strings). At offset 128
131 // the first message starts.
133 // Each message is terminated by a 0x1A byte. The next message follows
136 // The last message is followed by a 0x1A, too.
138 // 000000 6d 61 69 6c 73 65 72 76 65 72 2d 70 72 6f 6a 65 mailserver-proje
139 // 000010 63 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ct..............
140 // 000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
141 // 000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
142 // 000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
143 // 000050 00 00 00 00 00 00 36 30 34 37 35 37 32 45 3a 36 ......6047572E:6
144 // 000060 46 34 39 3a 46 4f 4c 30 31 33 35 35 00 00 00 00 F49:FOL01355....
145 // 000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
146 // 000080 52 65 74 75 72 6e 2d 50 61 74 68 3a 20 3c 75 72 Return-Path: <ur
148 // 000cb0 2d 2d 2d 2d 2d 2d 2d 2d 2d 2b 0d 0a 1a 52 65 74 ---------+...Ret
149 // 000cc0 75 72 6e 2d 50 61 74 68 3a 20 3c 62 6f 75 6e 63 urn-Path: <bounc
151 // 04dc50 46 30 33 38 44 2e 36 31 35 44 37 34 44 30 2d 2d F038D.615D74D0--
161 if (!f
.open(QIODevice::ReadOnly
)) {
162 inf
->alert(i18n("Unable to open %1, skipping", file
));
165 l
= f
.read((char *) &pmm_head
, sizeof(pmm_head
));
166 QString
folder("PegasusMail-Import/");
168 folder
.append(getFolderName((QString
)pmm_head
.id
));
170 folder
.append(pmm_head
.folder
);
172 inf
->addLog(i18n("Importing %1", QString("../") + QString(pmm_head
.folder
)));
174 QByteArray
input(MAX_LINE
,'\0');
175 bool first_msg
= true;
178 KTemporaryFile tempfile
;
180 inf
->setCurrent( (int) ( ( (float) f
.pos() / f
.size() ) * 100 ) );
183 // set the filepos back to last line minus the separate char (0x1a)
184 f
.seek(f
.pos() - l
+ 1);
187 // no problem to loose the last line in file. This only contains a separate char
188 while ( ! f
.atEnd() && (l
= f
.readLine(input
.data(),MAX_LINE
))) {
189 if (inf
->shouldTerminate()){
192 if ( input
.at( 0 ) == 0x1a ) {
195 tempfile
.write( input
, l
);
200 if(inf
->removeDupMsg
)
201 addMessage( inf
, folder
, tempfile
.fileName() );
203 addMessage_fastImport( inf
, folder
, tempfile
.fileName() );
212 /** imports a 'unix' format mail folder (*.MBX) */
213 void FilterPMail::importUnixMailFolder(const QString
& file
)
221 QString
folder("PegasusMail-Import/"), s(file
), separate
;
222 QByteArray
line(MAX_LINE
,'\0');
225 /** Get the folder name */
226 s
.replace( QRegExp("mbx$"), "pmg");
227 s
.replace( QRegExp("MBX$"), "PMG");
229 if (! f
.open( QIODevice::ReadOnly
) ) {
230 inf
->alert( i18n("Unable to open %1, skipping", s
) );
233 f
.read((char *) &pmg_head
, sizeof(pmg_head
));
237 folder
.append(getFolderName((QString
)pmg_head
.id
));
239 folder
.append(pmg_head
.folder
);
245 /** Read in the mbox */
247 if (! f
.open( QIODevice::ReadOnly
) ) {
248 inf
->alert( i18n("Unable to open %1, skipping", s
) );
250 inf
->addLog(i18n("Importing %1", QString("../") + QString(pmg_head
.folder
)));
251 l
= f
.readLine( line
.data(),MAX_LINE
); // read the first line which is unneeded
252 while ( ! f
.atEnd() ) {
253 KTemporaryFile tempfile
;
256 // we lost the last line, which is the first line of the new message in
257 // this lopp, but this is ok, because this is the separate line with
258 // "From ???@???" and we can forget them
259 while ( ! f
.atEnd() && (l
= f
.readLine(line
.data(),MAX_LINE
)) && ((separate
= line
.data()).left(5) != "From ")) {
260 tempfile
.write(line
.data(), l
);
261 if (inf
->shouldTerminate()){
266 if(inf
->removeDupMsg
)
267 addMessage( inf
, folder
, tempfile
.fileName() );
269 addMessage_fastImport( inf
, folder
, tempfile
.fileName() );
272 inf
->setCurrent(i18n("Message %1", n
));
273 inf
->setCurrent( (int) ( ( (float) f
.pos() / f
.size() ) * 100 ) );
279 /** Parse the information about folderstructure to folderMatrix */
280 bool FilterPMail::parseFolderMatrix()
282 kDebug() <<"Start parsing the foldermatrix.";
283 inf
->addLog(i18n("Parsing the folder structure..."));
285 QFile
hierarch(chosenDir
+ "/hierarch.pm");
286 if (! hierarch
.open( QIODevice::ReadOnly
) ) {
287 inf
->alert( i18n("Unable to open %1, skipping", chosenDir
+ "hierarch.pm" ) );
292 while ( !hierarch
.atEnd() ) {
293 tmpRead
= hierarch
.readLine();
294 if ( tmpRead
.isEmpty() )
297 tmpRead
.remove(tmpRead
.length() -2,2);
298 QStringList tmpList
= QString(tmpRead
).split(',', QString::SkipEmptyParts
);
300 for ( QStringList::Iterator it
= tmpList
.begin(); it
!= tmpList
.end(); ++it
, i
++) {
302 if(i
< 5) tmpArray
[i
] = _tmp
.remove('\"');
308 folderMatrix
.append(FolderStructure(tmpArray
));
315 /** get the foldername for a given file ID from folderMatrix */
316 QString
FilterPMail::getFolderName(QString ID
)
324 for ( FolderStructureIterator it
= folderMatrix
.begin(); it
!= folderMatrix
.end(); ++it
) {
325 FolderStructure tmp
= *it
;
327 QString _ID
= tmp
[2];
329 QString _type
= tmp
[0] + tmp
[1];
330 if(( _type
== "21")) {
335 folder
.prepend((tmp
[4] + '/'));