1 /* Copyright 2009,2010 Klarälvdalens Datakonsult AB
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License as
5 published by the Free Software Foundation; either version 2 of
6 the License or (at your option) version 3 or any later version
7 accepted by the membership of KDE e.V. (or its successor approved
8 by the membership of KDE e.V.), which shall act as a proxy
9 defined in Section 14 of version 3 of the license.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "filter_kmail_archive.h"
22 #include <KLocalizedString>
23 #include <KFileDialog>
27 #include <QApplication>
29 #include <QSharedPointer>
31 #include <boost/shared_ptr.hpp>
33 using namespace MailImporter
;
35 FilterKMailArchive::FilterKMailArchive()
36 : Filter( i18n( "Import KMail Archive File" ),
37 "Klar\xE4lvdalens Datakonsult AB",
38 i18n( "<p><b>KMail Archive File Import Filter</b></p>"
39 "<p>This filter will import archives files previously exported by KMail.</p>"
40 "<p>Archive files contain a complete folder subtree compressed into a single file.</p>" ) ),
46 FilterKMailArchive::~FilterKMailArchive()
50 // Input: .inbox.directory
52 // Can also return an empty string if this is no valid dir name
53 static QString
folderNameForDirectoryName( const QString
&dirName
)
55 Q_ASSERT( dirName
.startsWith( QLatin1String( "." ) ) );
56 const QString end
= ".directory";
57 const int expectedIndex
= dirName
.length() - end
.length();
58 if ( dirName
.toLower().indexOf( end
) != expectedIndex
)
60 QString returnName
= dirName
.left( dirName
.length() - end
.length() );
61 returnName
= returnName
.right( returnName
.length() - 1 );
65 bool FilterKMailArchive::importMessage( const KArchiveFile
*file
, const QString
&folderPath
)
67 if ( filterInfo()->shouldTerminate() )
70 qApp
->processEvents();
72 KMime::Message::Ptr
newMessage( new KMime::Message() );
73 newMessage
->setContent( file
->data() );
76 Akonadi::Collection collection
= parseFolderString( folderPath
);
77 if ( !collection
.isValid() ) {
78 filterInfo()->addErrorLogEntry( i18n( "Unable to retrieve folder for folder path %1.", folderPath
) );
82 if ( filterInfo()->removeDupMessage() ) {
83 KMime::Headers::MessageID
*messageId
= newMessage
->messageID( false );
84 if ( messageId
&&!messageId
->asUnicodeString().isEmpty() ) {
85 if ( checkForDuplicates( messageId
->asUnicodeString(), collection
, folderPath
) ) {
92 const bool result
= addAkonadiMessage( collection
, newMessage
);
99 bool FilterKMailArchive::importFolder( const KArchiveDirectory
*folder
, const QString
&folderPath
)
101 kDebug() << "Importing folder" << folder
->name();
102 filterInfo()->addInfoLogEntry( i18n( "Importing folder '%1'...", folderPath
) );
103 filterInfo()->setTo( filterInfo()->rootCollection().name() + folderPath
);
104 const KArchiveDirectory
* const messageDir
=
105 dynamic_cast<const KArchiveDirectory
*>( folder
->entry( "cur" ) );
108 int total
= messageDir
->entries().count();
111 foreach( const QString
&entryName
, messageDir
->entries() ) {
112 filterInfo()->setCurrent( cur
* 100 / total
);
113 filterInfo()->setOverall( mFilesDone
* 100 / mTotalFiles
);
114 const KArchiveEntry
* const entry
= messageDir
->entry( entryName
);
116 if ( entry
->isFile() ) {
117 const int oldCount
= mFilesDone
;
118 if ( !importMessage( static_cast<const KArchiveFile
*>( entry
), folderPath
) )
121 // Adjust the counter. Total count can decrease because importMessage() detects a duplicate
122 if ( oldCount
!= mFilesDone
)
128 filterInfo()->addErrorLogEntry( i18n( "Unexpected subfolder %1 in folder %2.", entryName
, folder
->name() ) );
133 filterInfo()->addErrorLogEntry( i18n( "No subfolder named 'cur' in folder %1.", folder
->name() ) );
138 bool FilterKMailArchive::importDirectory( const KArchiveDirectory
*directory
, const QString
&folderPath
)
140 kDebug() << "Importing directory" << directory
->name();
141 foreach( const QString
&entryName
, directory
->entries() ) {
142 const KArchiveEntry
* const entry
= directory
->entry( entryName
);
144 if ( entry
->isDirectory() ) {
146 const KArchiveDirectory
*dir
= static_cast<const KArchiveDirectory
*>( entry
);
148 if ( !dir
->name().startsWith( QLatin1String( "." ) ) ) {
149 if ( !importFolder( dir
, folderPath
+ QLatin1Char('/') + dir
->name() ) )
153 // Entry starts with a dot, so we assume it is a subdirectory
156 const QString folderName
= folderNameForDirectoryName( entry
->name() );
157 if ( folderName
.isEmpty() ) {
158 filterInfo()->addErrorLogEntry( i18n( "Unexpected subdirectory named '%1'.", entry
->name() ) );
162 if ( !importDirectory( dir
, folderPath
+ QLatin1Char('/') + folderName
) )
172 int FilterKMailArchive::countFiles( const KArchiveDirectory
*directory
) const
175 foreach( const QString
&entryName
, directory
->entries() ) {
176 const KArchiveEntry
* const entry
= directory
->entry( entryName
);
177 if ( entry
->isFile() )
180 count
+= countFiles( static_cast<const KArchiveDirectory
*>( entry
) );
185 void FilterKMailArchive::import()
187 Q_ASSERT( filterInfo()->rootCollection().isValid() );
189 KFileDialog
fileDialog( KUrl(), QString(), filterInfo()->parent() );
190 fileDialog
.setMode( KFile::File
| KFile::LocalOnly
);
191 fileDialog
.setCaption( i18n( "Select KMail Archive File to Import" ) );
192 fileDialog
.setFilter( "*.tar.bz2 *.tar.gz *.tar *.zip|" +
193 i18n( "KMail Archive Files (*.tar, *.tar.gz, *.tar.bz2, *.zip)" ) );
194 if ( !fileDialog
.exec() ) {
195 filterInfo()->alert( i18n( "Please select an archive file that should be imported." ) );
198 const QString archiveFile
= fileDialog
.selectedFile();
199 importMails( archiveFile
);
202 void FilterKMailArchive::importMails( const QString
&archiveFile
)
204 filterInfo()->setFrom( archiveFile
);
206 KMimeType::Ptr mimeType
= KMimeType::findByUrl( archiveFile
, 0, true /* local file */ );
207 typedef QSharedPointer
<KArchive
> KArchivePtr
;
209 if ( !mimeType
->patterns().filter( "tar", Qt::CaseInsensitive
).isEmpty() )
210 archive
= KArchivePtr( new KTar( archiveFile
) );
211 else if ( !mimeType
->patterns().filter( "zip", Qt::CaseInsensitive
).isEmpty() )
212 archive
= KArchivePtr( new KZip( archiveFile
) );
214 filterInfo()->alert( i18n( "The file '%1' does not appear to be a valid archive.", archiveFile
) );
218 if ( !archive
->open( QIODevice::ReadOnly
) ) {
219 filterInfo()->alert( i18n( "Unable to open archive file '%1'", archiveFile
) );
223 filterInfo()->setOverall( 0 );
224 filterInfo()->addInfoLogEntry( i18n( "Counting files in archive..." ) );
225 mTotalFiles
= countFiles( archive
->directory() );
227 if ( importDirectory( archive
->directory(), QString() ) ) {
228 filterInfo()->setOverall( 100 );
229 filterInfo()->setCurrent( 100 );
230 filterInfo()->addInfoLogEntry( i18n( "Importing the archive file '%1' into the folder '%2' succeeded.",
231 archiveFile
, filterInfo()->rootCollection().name() ) );
232 filterInfo()->addInfoLogEntry( i18np( "1 message was imported.", "%1 messages were imported.",
236 filterInfo()->addInfoLogEntry( i18n( "Importing the archive failed." ) );