1 /***************************************************************************
2 filters.cxx - description
4 begin : Fri Jun 30 2000
5 copyright : (C) 2000 by Hans Dijkema
6 email : kmailcvt@hum.org
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 ***************************************************************************/
17 /* Copyright (c) 2012 Montel Laurent <montel@kde.org> */
21 #include "filterinfo.h"
24 #include <KPIMUtils/KFileIO>
27 #include <Akonadi/CollectionFetchJob>
28 #include <Akonadi/Item>
29 #include <Akonadi/ItemCreateJob>
30 #include <Akonadi/ItemFetchJob>
31 #include <Akonadi/ItemFetchScope>
32 #include <Akonadi/CollectionCreateJob>
33 #include <akonadi/kmime/messageparts.h>
39 #include <KMessageBox>
41 #include <QScopedPointer>
44 //////////////////////////////////////////////////////////////////////////////////
46 // The generic filter class
48 //////////////////////////////////////////////////////////////////////////////////
50 using namespace MailImporter
;
55 Private(const QString
&_name
, const QString
&_author
, const QString
&_info
)
59 count_duplicates( 0 ),
70 QMultiMap
<QString
, QString
> messageFolderMessageIDMap
;
71 QMap
<QString
, Akonadi::Collection
> messageFolderCollectionMap
;
72 int count_duplicates
; //to count all duplicate messages
74 MailImporter::FilterInfo
*filterInfo
;
77 Filter::Filter( const QString
&name
, const QString
&author
,
79 : d( new Private( name
,author
,info
) )
90 d
->messageFolderMessageIDMap
.clear();
91 d
->messageFolderCollectionMap
.clear();
93 d
->count_duplicates
= 0;
96 void Filter::setMailDir( const QString
&mailDir
)
101 QString
Filter::mailDir() const
106 void Filter::setFilterInfo( FilterInfo
* info
)
108 d
->filterInfo
= info
;
112 MailImporter::FilterInfo
* Filter::filterInfo()
114 if( !d
->filterInfo
) {
115 qDebug()<<" filterInfo must never be null. You forgot to create a filterinfo";
117 return d
->filterInfo
;
120 void Filter::setCountDuplicates( int countDuplicate
)
122 d
->count_duplicates
= countDuplicate
;
125 int Filter::countDuplicates() const
127 return d
->count_duplicates
;
131 bool Filter::addAkonadiMessage( const Akonadi::Collection
&collection
,
132 const KMime::Message::Ptr
&message
, Akonadi::MessageStatus status
)
136 item
.setMimeType( QLatin1String("message/rfc822") );
138 if ( status
.isOfUnknownStatus() ) {
139 KMime::Headers::Base
*statusHeaders
= message
->headerByType( "X-Status" );
140 if( statusHeaders
) {
141 if( !statusHeaders
->isEmpty() ) {
142 status
.setStatusFromStr( statusHeaders
->asUnicodeString() );
143 item
.setFlags( status
.statusFlags() );
147 item
.setFlags( status
.statusFlags() );
150 item
.setPayload
<KMime::Message::Ptr
>( message
);
151 QScopedPointer
<Akonadi::ItemCreateJob
> job(new Akonadi::ItemCreateJob( item
, collection
));
152 job
->setAutoDelete( false );
154 d
->filterInfo
->alert( i18n( "<b>Error:</b> Could not add message to folder %1. Reason: %2",
155 collection
.name(), job
->errorString() ) );
161 QString
Filter::author() const
166 QString
Filter::name() const
171 QString
Filter::info() const
177 void Filter::setAuthor(const QString
&_author
)
182 void Filter::setName(const QString
&_name
)
186 void Filter::setInfo(const QString
&_info
)
193 Akonadi::Collection
Filter::parseFolderString(const QString
&folderParseString
)
195 // Return an already created collection:
196 QMap
<QString
, Akonadi::Collection
>::const_iterator
end( d
->messageFolderCollectionMap
.constEnd() );
197 for ( QMap
<QString
, Akonadi::Collection
>::const_iterator it
= d
->messageFolderCollectionMap
.constBegin(); it
!= end
; ++it
) {
198 if( it
.key() == folderParseString
)
202 // The folder hasn't yet been created, create it now.
203 const QStringList folderList
= folderParseString
.split( QLatin1Char('/'), QString::SkipEmptyParts
);
205 QString folderBuilder
;
206 Akonadi::Collection lastCollection
;
208 // Create each folder on the folder list and add it the map.
209 foreach( const QString
&folder
, folderList
) {
211 d
->messageFolderCollectionMap
[folder
] = addSubCollection( d
->filterInfo
->rootCollection(), folder
);
212 folderBuilder
= folder
;
213 lastCollection
= d
->messageFolderCollectionMap
[folder
];
216 folderBuilder
+= QLatin1Char( '/' ) + folder
;
217 d
->messageFolderCollectionMap
[folderBuilder
] = addSubCollection( lastCollection
, folder
);
218 lastCollection
= d
->messageFolderCollectionMap
[folderBuilder
];
222 return lastCollection
;
225 Akonadi::Collection
Filter::addSubCollection( const Akonadi::Collection
&baseCollection
,
226 const QString
&newCollectionPathName
)
228 // Ensure that the collection doesn't already exsit, if it does just return it.
229 Akonadi::CollectionFetchJob
*fetchJob
= new Akonadi::CollectionFetchJob( baseCollection
,
230 Akonadi::CollectionFetchJob::FirstLevel
);
231 if( !fetchJob
->exec() ) {
232 d
->filterInfo
->alert( i18n( "<b>Warning:</b> Could not check that the folder already exists. Reason: %1",
233 fetchJob
->errorString() ) );
234 return Akonadi::Collection();
237 foreach( const Akonadi::Collection
&subCollection
, fetchJob
->collections() ) {
238 if( subCollection
.name() == newCollectionPathName
) {
239 return subCollection
;
243 // The subCollection doesn't exsit, create a new one
244 Akonadi::Collection newSubCollection
;
245 newSubCollection
.setParentCollection( baseCollection
);
246 newSubCollection
.setName( newCollectionPathName
);
248 QScopedPointer
<Akonadi::CollectionCreateJob
> job(new Akonadi::CollectionCreateJob( newSubCollection
));
249 job
->setAutoDelete( false );
251 d
->filterInfo
->alert( i18n("<b>Error:</b> Could not create folder. Reason: %1",
252 job
->errorString() ) );
253 return Akonadi::Collection();
255 // Return the newly created collection
256 Akonadi::Collection collection
= job
->collection();
260 bool Filter::checkForDuplicates ( const QString
&msgID
,
261 const Akonadi::Collection
&msgCollection
,
262 const QString
&messageFolder
)
264 bool folderFound
= false;
266 // Check if the contents of this collection have already been found.
267 QMultiMap
<QString
, QString
>::const_iterator
end( d
->messageFolderMessageIDMap
.constEnd() );
268 for ( QMultiMap
<QString
, QString
>::const_iterator it
= d
->messageFolderMessageIDMap
.constBegin(); it
!= end
; ++it
) {
269 if( it
.key() == messageFolder
) {
276 // Populate the map with message IDs that are in that collection.
277 if( msgCollection
.isValid() ) {
278 Akonadi::ItemFetchJob
job( msgCollection
);
279 job
.fetchScope().fetchPayloadPart( Akonadi::MessagePart::Header
);
281 d
->filterInfo
->addInfoLogEntry( i18n( "<b>Warning:</b> Could not fetch mail in folder %1. Reason: %2"
282 " You may have duplicate messages.", messageFolder
, job
.errorString() ) );
284 foreach( const Akonadi::Item
&messageItem
, job
.items() ) {
285 if( !messageItem
.isValid() ) {
286 d
->filterInfo
->addInfoLogEntry( i18n( "<b>Warning:</b> Got an invalid message in folder %1.", messageFolder
) );
288 if( !messageItem
.hasPayload
<KMime::Message::Ptr
>() )
290 const KMime::Message::Ptr message
= messageItem
.payload
<KMime::Message::Ptr
>();
291 const KMime::Headers::Base
* messageID
= message
->messageID( false );
293 if( !messageID
->isEmpty() ) {
294 d
->messageFolderMessageIDMap
.insert( messageFolder
, messageID
->asUnicodeString() );
303 // Check if this message has a duplicate
304 QMultiMap
<QString
, QString
>::const_iterator
endMsgID( d
->messageFolderMessageIDMap
.constEnd() );
305 for ( QMultiMap
<QString
, QString
>::const_iterator it
= d
->messageFolderMessageIDMap
.constBegin();it
!=endMsgID
; ++it
) {
306 if( it
.key() == messageFolder
&&
307 it
.value() == msgID
)
311 // The message isn't a duplicate, but add it to the map for checking in the future.
312 d
->messageFolderMessageIDMap
.insert( messageFolder
, msgID
);
317 bool Filter::addMessage( const QString
&folderName
,
318 const QString
&msgPath
,
319 Akonadi::MessageStatus status
)
322 return doAddMessage( folderName
, msgPath
, true, status
);
325 bool Filter::addMessage_fastImport( const QString
&folderName
,
326 const QString
&msgPath
,
327 Akonadi::MessageStatus status
331 return doAddMessage( folderName
, msgPath
,false, status
);
334 bool Filter::doAddMessage( const QString
&folderName
,
335 const QString
&msgPath
,
337 Akonadi::MessageStatus status
)
340 // Create the mail folder (if not already created).
341 Akonadi::Collection mailFolder
= parseFolderString(folderName
);
343 KUrl
msgUrl( msgPath
);
344 if( !msgUrl
.isEmpty() &&msgUrl
.isLocalFile() ) {
346 // Read in the temporary file.
347 const QByteArray msgText
=
348 KPIMUtils::kFileToByteArray( msgUrl
.toLocalFile(), true, false );
349 if( msgText
.isEmpty() ) {
350 d
->filterInfo
->addErrorLogEntry( i18n( "Error: failed to read temporary file at %1", msgPath
) );
354 // Construct a message.
355 KMime::Message::Ptr
newMessage( new KMime::Message() );
356 newMessage
->setContent( msgText
);
359 if( duplicateCheck
) {
360 // Get the messageID.
361 const KMime::Headers::Base
* messageIDHeader
= newMessage
->messageID( false );
362 if( messageIDHeader
)
363 messageID
= messageIDHeader
->asUnicodeString();
365 if( !messageID
.isEmpty() ) {
366 // Check for duplicate.
367 if( checkForDuplicates( messageID
, mailFolder
, folderName
) ) {
368 d
->count_duplicates
++;
374 // Add it to the collection.
375 if( mailFolder
.isValid() ) {
376 addAkonadiMessage( mailFolder
, newMessage
, status
);
378 d
->filterInfo
->alert( i18n( "<b>Warning:</b> Got a bad message folder, adding to root folder." ) );
379 addAkonadiMessage( d
->filterInfo
->rootCollection(), newMessage
, status
);
385 int Filter::countDirectory(const QDir
&dir
, bool searchHiddenDirectory
)
389 if ( searchHiddenDirectory
)
390 subDirs
= dir
.entryList(QStringList(QLatin1String("*")), QDir::Dirs
| QDir::Hidden
, QDir::Name
);
392 subDirs
= dir
.entryList(QStringList(QLatin1String("[^\\.]*")), QDir::Dirs
, QDir::Name
); // Removal of . and ..
394 QStringList::ConstIterator end
= subDirs
.constEnd();
395 for (QStringList::ConstIterator filename
= subDirs
.constBegin() ; filename
!= end
; ++filename
) {
396 if(!(*filename
== QLatin1String( "." ) || *filename
== QLatin1String( ".." ))) {
397 countDir
+= countDirectory( QDir( dir
.filePath(*filename
) ), searchHiddenDirectory
) + 1;