1 /***************************************************************************
2 filter_thunderbird.cxx - Thunderbird mail import
5 copyright : (C) 2005 by Danny Kukawka
6 email : 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 "filter_thunderbird.hxx"
21 #include <kfiledialog.h>
22 #include <ktemporaryfile.h>
25 /** Default constructor. */
26 FilterThunderbird::FilterThunderbird(void) :
27 Filter(i18n("Import Thunderbird/Mozilla Local Mails and Folder Structure"),
29 i18n("<p><b>Thunderbird/Mozilla import filter</b></p>"
30 "<p>Select your base Thunderbird/Mozilla mailfolder"
31 " (usually ~/.thunderbird/*.default/Mail/Local Folders/).</p>"
32 "<p><b>Note:</b> Never choose a Folder which <u>does not</u> contain mbox-files (for example,"
33 " a maildir): if you do, you will get many new folders.</p>"
34 "<p>Since it is possible to recreate the folder structure, the folders "
35 "will be stored under: \"Thunderbird-Import\".</p>"))
39 FilterThunderbird::~FilterThunderbird(void)
43 /** Recursive import of Evolution's mboxes. */
44 void FilterThunderbird::import(FilterInfo
*info
)
47 * We ask the user to choose Evolution's root directory.
48 * This should be usually ~/.thunderbird/xxxx.default/Mail/Local Folders/
50 QString thunderDir
= QDir::homePath() + "/.thunderbird/";
53 thunderDir
= QDir::homePath();
57 kfd
= new KFileDialog( thunderDir
, "", 0 );
58 kfd
->setMode(KFile::Directory
| KFile::LocalOnly
);
60 mailDir
= kfd
->selectedFile();
62 if (mailDir
.isEmpty()) {
63 info
->alert(i18n("No directory selected."));
66 * If the user only select homedir no import needed because
67 * there should be no files and we surely import wrong files.
69 else if ( mailDir
== QDir::homePath() || mailDir
== (QDir::homePath() + '/')) {
70 info
->addLog(i18n("No files found for import."));
74 /** Recursive import of the MailArchives */
76 const QStringList rootSubDirs
= dir
.entryList(QStringList("[^\\.]*"), QDir::Dirs
, QDir::Name
); // Removal of . and ..
77 int currentDir
= 1, numSubDirs
= rootSubDirs
.size();
78 for(QStringList::ConstIterator filename
= rootSubDirs
.constBegin() ; filename
!= rootSubDirs
.constEnd() ; ++filename
, ++currentDir
) {
79 if(info
->shouldTerminate()) break;
80 importDirContents(info
, dir
.filePath(*filename
), *filename
, *filename
);
81 info
->setOverall((int) ((float) currentDir
/ numSubDirs
* 100));
84 /** import last but not least all archives from the root-dir */
85 QDir
importDir (mailDir
);
86 const QStringList files
= importDir
.entryList(QStringList("[^\\.]*"), QDir::Files
, QDir::Name
);
87 for ( QStringList::ConstIterator mailFile
= files
.constBegin(); mailFile
!= files
.constEnd(); ++mailFile
) {
88 if(info
->shouldTerminate()) break;
89 QString temp_mailfile
= *mailFile
;
90 if (temp_mailfile
.endsWith(QLatin1String(".msf")) || temp_mailfile
.endsWith(QLatin1String("msgFilterRules.dat"))) {}
92 info
->addLog( i18n("Start import file %1...", temp_mailfile
) );
93 importMBox(info
, mailDir
+ temp_mailfile
, temp_mailfile
, QString());
97 info
->addLog( i18n("Finished importing emails from %1", mailDir
));
98 if(count_duplicates
> 0) {
99 info
->addLog( i18np("1 duplicate message not imported", "%1 duplicate messages not imported", count_duplicates
));
102 if (info
->shouldTerminate()) info
->addLog( i18n("Finished import, canceled by user."));
103 info
->setCurrent(100);
104 info
->setOverall(100);
109 * Import of a directory contents.
110 * @param info Information storage for the operation.
111 * @param dirName The name of the directory to import.
112 * @param KMailRootDir The directory's root directory in KMail's folder structure.
113 * @param KMailSubDir The directory's direct ancestor in KMail's folder structure.
115 void FilterThunderbird::importDirContents(FilterInfo
*info
, const QString
& dirName
, const QString
& KMailRootDir
, const QString
& KMailSubDir
)
117 if(info
->shouldTerminate()) return;
118 /** Here Import all archives in the current dir */
121 QDir
importDir (dirName
);
122 const QStringList files
= importDir
.entryList(QStringList("[^\\.]*"), QDir::Files
, QDir::Name
);
123 for ( QStringList::ConstIterator mailFile
= files
.constBegin(); mailFile
!= files
.constEnd(); ++mailFile
) {
124 if(info
->shouldTerminate()) break;
125 QString temp_mailfile
= *mailFile
;
126 if (temp_mailfile
.endsWith(QLatin1String(".msf")) || temp_mailfile
.endsWith(QLatin1String("msgFilterRules.dat"))) {}
128 info
->addLog( i18n("Start import file %1...", temp_mailfile
) );
129 importMBox(info
, (dirName
+ '/' + temp_mailfile
) , KMailRootDir
, KMailSubDir
);
133 /** If there are subfolders, we import them one by one */
134 QDir
subfolders(dirName
);
135 const QStringList subDirs
= subfolders
.entryList(QStringList("[^\\.]*"), QDir::Dirs
, QDir::Name
);
136 for(QStringList::ConstIterator filename
= subDirs
.constBegin() ; filename
!= subDirs
.constEnd() ; ++filename
) {
137 if(info
->shouldTerminate()) break;
139 if(!KMailSubDir
.isNull()) {
140 kSubDir
= KMailSubDir
+ '/' + *filename
;
144 importDirContents(info
, subfolders
.filePath(*filename
), KMailRootDir
, kSubDir
);
149 * Import of a MBox file.
150 * @param info Information storage for the operation.
151 * @param dirName The MBox's name.
152 * @param KMailRootDir The directory's root directory in KMail's folder structure.
153 * @param KMailSubDir The directory's equivalent in KMail's folder structure. *
155 void FilterThunderbird::importMBox(FilterInfo
*info
, const QString
& mboxName
, const QString
& rootDir
, const QString
& targetDir
)
157 QFile
mbox(mboxName
);
158 bool first_msg
= true;
159 if (!mbox
.open(QIODevice::ReadOnly
)) {
160 info
->alert(i18n("Unable to open %1, skipping", mboxName
));
162 QFileInfo
filenameInfo(mboxName
);
165 if( mboxName
.length() > 20 ) {
166 QString tmp_info
= mboxName
;
167 tmp_info
= tmp_info
.replace( mailDir
, "../" );
168 if (tmp_info
.contains(".sbd"))
169 tmp_info
.remove(".sbd");
170 info
->setFrom( tmp_info
);
172 info
->setFrom(mboxName
);
173 if(targetDir
.contains(".sbd")) {
174 QString tmp_info
= targetDir
;
175 tmp_info
.remove(".sbd");
176 info
->setTo(tmp_info
);
178 info
->setTo(targetDir
);
180 QByteArray
input(MAX_LINE
,'\0');
183 while (!mbox
.atEnd()) {
186 /** @todo check if the file is really a mbox, maybe search for 'from' string at start */
188 * Don't use QTextStream to read from mbox, better use QDataStream. QTextStream only
189 * support Unicode/Latin1/Locale. So you lost information from emails with
190 * charset!=Unicode/Latin1/Locale (e.g. KOI8-R) and Content-Transfer-Encoding != base64
191 * (e.g. 8Bit). It also not help to convert the QTextStream to Unicode. By this you
192 * get Unicode/UTF-email but KMail can't detect the correct charset.
197 tmp
.write( input
, l
);
198 l
= mbox
.readLine( input
.data(),MAX_LINE
); // read the first line, prevent "From "
199 tmp
.write( input
, l
);
201 while ( ! mbox
.atEnd() && (l
= mbox
.readLine(input
.data(),MAX_LINE
)) && ((separate
= input
.data()).left(5) != "From ")) {
202 tmp
.write( input
, l
);
208 QString _targetDir
= targetDir
;
209 if(!targetDir
.isNull()) {
210 if(_targetDir
.contains(".sbd"))
211 _targetDir
.remove(".sbd");
212 destFolder
+= "Thunderbird-Import/" + _targetDir
+ '/' + filenameInfo
.completeBaseName();// mboxName;
214 destFolder
= "Thunderbird-Import/" + rootDir
;
215 if(destFolder
.contains(".sbd"))
216 destFolder
.remove(".sbd");
219 if(info
->removeDupMsg
)
220 addMessage( info
, destFolder
, tmp
.fileName() );
222 addMessage_fastImport( info
, destFolder
, tmp
.fileName() );
224 int currentPercentage
= (int) (((float) mbox
.pos() / filenameInfo
.size()) * 100);
225 info
->setCurrent(currentPercentage
);
226 if (info
->shouldTerminate()) {