split message into several paras and fix file extension markup
[kdepim.git] / mailimporter / filters.cpp
blob69c5f4d4cab9c550b9b3940e642ee476b3539611
1 /***************************************************************************
2 filters.cxx - description
3 -------------------
4 begin : Fri Jun 30 2000
5 copyright : (C) 2000 by Hans Dijkema
6 email : kmailcvt@hum.org
7 ***************************************************************************/
9 /***************************************************************************
10 * *
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. *
15 * *
16 ***************************************************************************/
17 /* Copyright (c) 2012 Montel Laurent <montel@kde.org> */
19 // Local Includes
20 #include "filters.h"
21 #include "filterinfo.h"
23 // KDEPIM Includes
24 #include <KPIMUtils/KFileIO>
26 // Akonadi Includes
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>
35 // KDE Includes
36 #include <KUrl>
37 #include <KLocale>
38 #include <KDebug>
39 #include <KMessageBox>
41 #include <QScopedPointer>
44 //////////////////////////////////////////////////////////////////////////////////
46 // The generic filter class
48 //////////////////////////////////////////////////////////////////////////////////
50 using namespace MailImporter;
52 class Filter::Private
54 public:
55 Private(const QString &_name, const QString &_author, const QString &_info)
56 : name( _name ),
57 author( _author ),
58 info( _info ),
59 count_duplicates( 0 ),
60 filterInfo( 0 )
63 ~Private()
66 QString name;
67 QString author;
68 QString info;
69 QString mailDir;
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,
78 const QString &info )
79 : d( new Private( name,author,info ) )
83 Filter::~Filter()
85 delete d;
88 void Filter::clear()
90 d->messageFolderMessageIDMap.clear();
91 d->messageFolderCollectionMap.clear();
92 d->mailDir.clear();
93 d->count_duplicates = 0;
96 void Filter::setMailDir( const QString &mailDir )
98 d->mailDir = mailDir;
101 QString Filter::mailDir() const
103 return d->mailDir;
106 void Filter::setFilterInfo( FilterInfo* info )
108 d->filterInfo = info;
109 clear();
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 )
134 Akonadi::Item item;
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() );
146 } else {
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 );
153 if( !job->exec() ) {
154 d->filterInfo->alert( i18n( "<b>Error:</b> Could not add message to folder %1. Reason: %2",
155 collection.name(), job->errorString() ) );
156 return false;
158 return true;
161 QString Filter::author() const
163 return d->author;
166 QString Filter::name() const
168 return d->name;
171 QString Filter::info() const
173 return d->info;
177 void Filter::setAuthor(const QString &_author)
179 d->author = _author;
182 void Filter::setName(const QString &_name)
184 d->name = _name;
186 void Filter::setInfo(const QString &_info)
188 d->info = _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 )
199 return it.value();
202 // The folder hasn't yet been created, create it now.
203 const QStringList folderList = folderParseString.split( QLatin1Char('/'), QString::SkipEmptyParts );
204 bool isFirst = true;
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 ) {
210 if( isFirst ) {
211 d->messageFolderCollectionMap[folder] = addSubCollection( d->filterInfo->rootCollection(), folder );
212 folderBuilder = folder;
213 lastCollection = d->messageFolderCollectionMap[folder];
214 isFirst = false;
215 } else {
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 );
250 if( !job->exec() ) {
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();
257 return 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 ) {
270 folderFound = true;
271 break;
275 if( !folderFound ) {
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 );
280 if( !job.exec() ) {
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() ) );
283 } else {
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 ) );
287 } else {
288 if( !messageItem.hasPayload<KMime::Message::Ptr>() )
289 continue;
290 const KMime::Message::Ptr message = messageItem.payload<KMime::Message::Ptr>();
291 const KMime::Headers::Base* messageID = message->messageID( false );
292 if( messageID ) {
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 )
308 return true;
311 // The message isn't a duplicate, but add it to the map for checking in the future.
312 d->messageFolderMessageIDMap.insert( messageFolder, msgID );
313 return false;
317 bool Filter::addMessage( const QString &folderName,
318 const QString &msgPath,
319 Akonadi::MessageStatus status )
321 // Add the message.
322 return doAddMessage( folderName, msgPath, true, status );
325 bool Filter::addMessage_fastImport( const QString &folderName,
326 const QString &msgPath,
327 Akonadi::MessageStatus status
330 // Add the message.
331 return doAddMessage( folderName, msgPath,false, status );
334 bool Filter::doAddMessage( const QString &folderName,
335 const QString &msgPath,
336 bool duplicateCheck,
337 Akonadi::MessageStatus status )
339 QString messageID;
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 ) );
351 return false;
354 // Construct a message.
355 KMime::Message::Ptr newMessage( new KMime::Message() );
356 newMessage->setContent( msgText );
357 newMessage->parse();
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++;
369 return false;
374 // Add it to the collection.
375 if( mailFolder.isValid() ) {
376 addAkonadiMessage( mailFolder, newMessage, status );
377 } else {
378 d->filterInfo->alert( i18n( "<b>Warning:</b> Got a bad message folder, adding to root folder." ) );
379 addAkonadiMessage( d->filterInfo->rootCollection(), newMessage, status );
382 return true;
385 int Filter::countDirectory(const QDir &dir, bool searchHiddenDirectory)
387 int countDir = 0;
388 QStringList subDirs;
389 if ( searchHiddenDirectory )
390 subDirs = dir.entryList(QStringList(QLatin1String("*")), QDir::Dirs | QDir::Hidden, QDir::Name);
391 else
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;
400 return countDir;
403 // vim: ts=2 sw=2 et