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 ***************************************************************************/
18 #include "filters.hxx"
22 #include <KPIMUtils/KFileIO>
25 #include <Akonadi/CollectionFetchJob>
26 #include <Akonadi/Item>
27 #include <Akonadi/ItemCreateJob>
28 #include <Akonadi/ItemFetchJob>
29 #include <Akonadi/ItemFetchScope>
30 #include <Akonadi/CollectionCreateJob>
31 #include <akonadi/kmime/messageparts.h>
32 #include <akonadi/kmime/messagestatus.h>
38 #include <KMessageBox>
42 //////////////////////////////////////////////////////////////////////////////////
44 // The API to the kmailcvt dialog --> Gives the import filter access to
45 // put information on the dialog.
47 //////////////////////////////////////////////////////////////////////////////////
49 bool FilterInfo::s_terminateASAP
= false;
51 FilterInfo::FilterInfo( KImportPageDlg
* dlg
, QWidget
* parent
, bool _removeDupMsg
)
55 removeDupMsg
= _removeDupMsg
;
56 s_terminateASAP
= false;
59 FilterInfo::~FilterInfo()
63 void FilterInfo::setStatusMsg( const QString
& status
)
65 m_dlg
->_textStatus
->setText( status
);
68 void FilterInfo::setFrom( const QString
& from
)
70 m_dlg
->_from
->setText( from
);
73 void FilterInfo::setTo( const QString
& to
)
75 m_dlg
->_to
->setText( to
);
78 void FilterInfo::setCurrent( const QString
& current
)
80 m_dlg
->_current
->setText( current
);
81 kapp
->processEvents();
84 void FilterInfo::setCurrent( int percent
)
86 m_dlg
->_done_current
->setValue( percent
);
87 kapp
->processEvents(); // Be careful - back & finish buttons disabled, so only user event that can happen is cancel/close button
90 void FilterInfo::setOverall( int percent
)
92 m_dlg
->_done_overall
->setValue( percent
);
95 void FilterInfo::addLog( const QString
& log
)
97 m_dlg
->_log
->addItem( log
);
98 m_dlg
->_log
->setCurrentItem( m_dlg
->_log
->item(m_dlg
->_log
->count() - 1 ));
99 kapp
->processEvents();
102 void FilterInfo::clear()
104 m_dlg
->_log
->clear();
107 setCurrent( QString() );
108 setFrom( QString() );
112 void FilterInfo::alert( const QString
& message
)
114 KMessageBox::information( m_parent
, message
);
117 void FilterInfo::terminateASAP()
119 s_terminateASAP
= true;
122 bool FilterInfo::shouldTerminate()
124 return s_terminateASAP
;
127 Akonadi::Collection
FilterInfo::rootCollection() const
129 return m_rootCollection
;
132 void FilterInfo::setRootCollection( const Akonadi::Collection
&collection
)
134 m_rootCollection
= collection
;
138 //////////////////////////////////////////////////////////////////////////////////
140 // The generic filter class
142 //////////////////////////////////////////////////////////////////////////////////
145 Filter::Filter( const QString
& name
, const QString
& author
,
146 const QString
& info
)
152 count_duplicates
= 0;
155 bool Filter::addAkonadiMessage( FilterInfo
* info
, const Akonadi::Collection
&collection
,
156 const KMime::Message::Ptr
& message
)
159 Akonadi::MessageStatus status
;
161 item
.setMimeType( "message/rfc822" );
163 KMime::Headers::Base
*statusHeaders
= message
->headerByType( "X-Status" );
164 if( statusHeaders
) {
165 if( !statusHeaders
->isEmpty() ) {
166 status
.setStatusFromStr( statusHeaders
->asUnicodeString() );
167 item
.setFlags( status
.statusFlags() );
170 item
.setPayload
<KMime::Message::Ptr
>( message
);
171 Akonadi::ItemCreateJob
* job
= new Akonadi::ItemCreateJob( item
, collection
);
173 info
->alert( i18n( "<b>Error:</b> Could not add message to folder %1. Reason: %2",
174 collection
.name(), job
->errorString() ) );
180 Akonadi::Collection
Filter::parseFolderString(FilterInfo
* info
, const QString
& folderParseString
)
182 // Return an already created collection:
183 for( QMap
<QString
, Akonadi::Collection
>::const_iterator it
=
184 m_messageFolderCollectionMap
.constBegin(); it
!= m_messageFolderCollectionMap
.constEnd(); it
++ ) {
185 if( it
.key() == folderParseString
)
189 // The folder hasn't yet been created, create it now.
190 const QStringList folderList
= folderParseString
.split( '/', QString::SkipEmptyParts
);
192 QString folderBuilder
;
193 Akonadi::Collection lastCollection
;
195 // Create each folder on the folder list and add it the map.
196 foreach( const QString
&folder
, folderList
) {
198 m_messageFolderCollectionMap
[folder
] = addSubCollection( info
, info
->rootCollection(), folder
);
199 folderBuilder
= folder
;
200 lastCollection
= m_messageFolderCollectionMap
[folder
];
203 folderBuilder
+= '/' + folder
;
204 m_messageFolderCollectionMap
[folderBuilder
] = addSubCollection( info
, lastCollection
, folder
);
205 lastCollection
= m_messageFolderCollectionMap
[folderBuilder
];
209 return lastCollection
;
212 Akonadi::Collection
Filter::addSubCollection( FilterInfo
* info
,
213 const Akonadi::Collection
&baseCollection
,
214 const QString
& newCollectionPathName
)
216 // Ensure that the collection doesn't already exsit, if it does just return it.
217 Akonadi::CollectionFetchJob
*fetchJob
= new Akonadi::CollectionFetchJob( baseCollection
,
218 Akonadi::CollectionFetchJob::FirstLevel
);
219 if( !fetchJob
->exec() ) {
220 info
->alert( i18n( "<b>Warning:</b> Could not check that the folder already exists. Reason: %1",
221 fetchJob
->errorString() ) );
222 return Akonadi::Collection();
225 foreach( const Akonadi::Collection
&subCollection
, fetchJob
->collections() ) {
226 if( subCollection
.name() == newCollectionPathName
) {
227 return subCollection
;
231 // The subCollection doesn't exsit, create a new one
232 Akonadi::Collection newSubCollection
;
233 newSubCollection
.setParentCollection( baseCollection
);
234 newSubCollection
.setName( newCollectionPathName
);
236 Akonadi::CollectionCreateJob
* job
= new Akonadi::CollectionCreateJob( newSubCollection
);
238 info
->alert( i18n("<b>Error:</b> Could not create folder. Reason: %1",
239 job
->errorString() ) );
240 return Akonadi::Collection();
242 // Return the newly created collection
243 return job
->collection();
246 bool Filter::checkForDuplicates ( FilterInfo
* info
, const QString
& msgID
,
247 const Akonadi::Collection
& msgCollection
,
248 const QString
& messageFolder
)
250 bool folderFound
= false;
252 // Check if the contents of this collection have already been found.
253 for( QMultiMap
<QString
, QString
>::const_iterator it
= m_messageFolderMessageIDMap
.constBegin();
254 it
!= m_messageFolderMessageIDMap
.constEnd(); it
++ ) {
255 if( it
.key() == messageFolder
) {
262 // Populate the map with message IDs that are in that collection.
263 if( msgCollection
.isValid() ) {
264 Akonadi::ItemFetchJob
job( msgCollection
);
265 job
.fetchScope().fetchPayloadPart( Akonadi::MessagePart::Header
);
267 info
->addLog( i18n( "<b>Warning:</b> Could not fetch mail in folder %1. Reason: %2"
268 " You may have duplicate messages.", messageFolder
, job
.errorString() ) );
270 foreach( const Akonadi::Item
& messageItem
, job
.items() ) {
271 if( !messageItem
.isValid() ) {
272 info
->addLog( i18n( "<b>Warning:</b> Got an invalid message in folder %1.", messageFolder
) );
274 if( !messageItem
.hasPayload
<KMime::Message::Ptr
>() )
276 const KMime::Message::Ptr message
= messageItem
.payload
<KMime::Message::Ptr
>();
277 const KMime::Headers::Base
* messageID
= message
->messageID( false );
279 if( !messageID
->isEmpty() ) {
280 m_messageFolderMessageIDMap
.insert( messageFolder
, messageID
->asUnicodeString() );
289 // Check if this message has a duplicate
290 for( QMultiMap
<QString
, QString
>::const_iterator it
= m_messageFolderMessageIDMap
.constBegin();
291 it
!= m_messageFolderMessageIDMap
.constEnd(); it
++ ) {
292 if( it
.key() == messageFolder
&&
293 it
.value() == msgID
)
297 // The message isn't a duplicate, but add it to the map for checking in the future.
298 m_messageFolderMessageIDMap
.insert( messageFolder
, msgID
);
303 bool Filter::addMessage( FilterInfo
* info
, const QString
& folderName
,
304 const QString
& msgPath
,
305 const QString
& msgStatusFlags
// Defunct - KMime will now handle the MessageStatus flags.
308 Q_UNUSED( msgStatusFlags
);
311 return doAddMessage( info
, folderName
, msgPath
, true );
314 bool Filter::addMessage_fastImport( FilterInfo
* info
, const QString
& folderName
,
315 const QString
& msgPath
,
316 const QString
& msgStatusFlags
// Defunct - KMime will now handle the MessageStatus flags.
319 Q_UNUSED( msgStatusFlags
);
322 return doAddMessage( info
, folderName
, msgPath
);
325 bool Filter::doAddMessage( FilterInfo
* info
, const QString
& folderName
,
326 const QString
& msgPath
,
327 bool duplicateCheck
)
330 // Create the mail folder (if not already created).
331 Akonadi::Collection mailFolder
= parseFolderString( info
, folderName
);
333 KUrl
msgUrl( msgPath
);
334 if( !msgUrl
.isEmpty() && msgUrl
.isLocalFile() ) {
336 // Read in the temporary file.
337 const QByteArray msgText
=
338 KPIMUtils::kFileToByteArray( msgUrl
.toLocalFile(), true, false );
339 if( msgText
.isEmpty() ) {
340 info
->addLog( i18n( "Error: failed to read temporary file at %1", msgPath
) );
344 // Construct a message.
345 KMime::Message::Ptr
newMessage( new KMime::Message() );
346 newMessage
->setContent( msgText
);
349 if( duplicateCheck
) {
350 // Get the messageID.
351 const KMime::Headers::Base
* messageIDHeader
= newMessage
->messageID( false );
352 if( messageIDHeader
)
353 messageID
= messageIDHeader
->asUnicodeString();
355 if( !messageID
.isEmpty() ) {
356 // Check for duplicate.
357 if( checkForDuplicates( info
, messageID
, mailFolder
, folderName
) ) {
364 // Add it to the collection.
365 if( mailFolder
.isValid() ) {
366 addAkonadiMessage( info
, mailFolder
, newMessage
);
368 info
->alert( i18n( "<b>Warning:</b> Got a bad message folder, adding to root folder." ) );
369 addAkonadiMessage( info
, info
->rootCollection(), newMessage
);
376 bool Filter::needsSecondPage()