fix errors found while translating
[kdepim.git] / kmail / searchjob.cpp
blob44b8911cdb9eead66666562eafd31f7cd7d0a60f
1 /*
2 * Copyright (c) 2004 Carsten Burghardt <burghardt@kde.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * In addition, as a special exception, the copyright holders give
18 * permission to link the code of this program with any edition of
19 * the Qt library by Trolltech AS, Norway (or with modified versions
20 * of Qt that use the same license as Qt), and distribute linked
21 * combinations including the two. You must obey the GNU General
22 * Public License in all respects for all of the code used other than
23 * Qt. If you modify this file, you may extend this exception to
24 * your version of the file, but you are not obligated to do so. If
25 * you do not wish to do so, delete this exception statement from
26 * your version.
30 #include "searchjob.h"
31 #include "kmfolderimap.h"
32 #include "imapaccountbase.h"
33 #include "kmsearchpattern.h"
34 #include "kmfolder.h"
35 #include "imapjob.h"
36 #include "kmmsgdict.h"
38 #include <progressmanager.h>
40 using KPIM::ProgressItem;
41 using KPIM::ProgressManager;
43 #include <kdebug.h>
44 #include <kurl.h>
45 #include <kio/scheduler.h>
46 #include <kio/job.h>
47 #include <kio/global.h>
48 #include <klocale.h>
49 #include <kmessagebox.h>
50 #include <QTextDocument>
52 namespace KMail {
54 SearchJob::SearchJob( KMFolderImap* folder, ImapAccountBase* account,
55 const KMSearchPattern* pattern, quint32 serNum )
56 : FolderJob( 0, tOther, (folder ? folder->folder() : 0) ),
57 mFolder( folder ), mAccount( account ), mSearchPattern( pattern ),
58 mSerNum( serNum ), mRemainingMsgs( 0 ), mProgress( 0 ),
59 mUngetCurrentMsg( false )
63 SearchJob::~SearchJob()
67 void SearchJob::execute()
69 if ( mSerNum == 0 )
71 searchCompleteFolder();
72 } else {
73 searchSingleMessage();
77 //-----------------------------------------------------------------------------
78 void SearchJob::searchCompleteFolder()
80 // generate imap search command and save local search patterns
81 QString searchString = searchStringFromPattern( mSearchPattern );
83 if ( searchString.isEmpty() ) // skip imap search and download the messages
84 return slotSearchData( 0, QString(),QString() );
86 // do the IMAP search
87 KUrl url = mAccount->getUrl();
88 url.setPath( mFolder->imapPath() + ";SECTION=" + searchString );
89 QByteArray packedArgs;
90 QDataStream stream( &packedArgs, QIODevice::WriteOnly );
91 stream << (int) 'E' << url;
92 KIO::SimpleJob *job = KIO::special( url, packedArgs, KIO::HideProgressInfo );
93 if ( mFolder->imapPath() != QString("/") )
94 { // the "/ folder" of an imap account makes the kioslave stall
95 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
96 connect( job, SIGNAL(infoMessage(KJob*,const QString&,const QString&)),
97 SLOT(slotSearchData(KJob*,const QString&,const QString&)) );
98 connect( job, SIGNAL(result(KJob *)),
99 SLOT(slotSearchResult(KJob *)) );
101 else
102 { // for the "/ folder" of an imap account, searching blocks the kioslave
103 slotSearchData( job, QString(), QString() );
104 slotSearchResult( job );
108 //-----------------------------------------------------------------------------
109 QString SearchJob::searchStringFromPattern( const KMSearchPattern* pattern )
111 QStringList parts;
112 // this is for the search pattern that can only be done local
113 mLocalSearchPattern = new KMSearchPattern();
114 mLocalSearchPattern->setOp( pattern->op() );
116 QList<KMSearchRule*>::const_iterator it;
117 for ( it = pattern->begin() ; it != pattern->end() ; ++it )
119 // construct an imap search command
120 bool accept = true;
121 QString result;
122 QString field = (*it)->field();
123 // check if the operation is supported
124 if ( (*it)->function() == KMSearchRule::FuncContainsNot ) {
125 result = "NOT ";
126 } else if ( (*it)->function() == KMSearchRule::FuncIsGreater &&
127 (*it)->field() == "<size>" ) {
128 result = "LARGER ";
129 } else if ( (*it)->function() == KMSearchRule::FuncIsLess &&
130 (*it)->field() == "<size>" ) {
131 result = "SMALLER ";
132 } else if ( (*it)->function() != KMSearchRule::FuncContains ) {
133 // can't be handled by imap
134 accept = false;
137 // now see what should be searched
138 if ( (*it)->field() == "<message>" ) {
139 result += "TEXT \"" + (*it)->contents() + "\"";
140 } else if ( (*it)->field() == "<body>" ) {
141 result += "BODY \"" + (*it)->contents() + "\"";
142 } else if ( (*it)->field() == "<recipients>" ) {
143 result += " (OR HEADER To \"" + (*it)->contents() + "\" HEADER Cc \"" +
144 (*it)->contents() + "\" HEADER Bcc \"" + (*it)->contents() + "\")";
145 } else if ( (*it)->field() == "<size>" ) {
146 result += (*it)->contents();
147 } else if ( (*it)->field() == "<age in days>" ||
148 (*it)->field() == "<status>" ||
149 (*it)->field() == "<any header>" ) {
150 accept = false;
151 } else {
152 result += "HEADER "+ field + " \"" + (*it)->contents() + "\"";
155 if ( result.isEmpty() ) {
156 accept = false;
159 if ( accept ) {
160 parts += result;
161 } else {
162 mLocalSearchPattern->append( *it );
166 QString search;
167 if ( !parts.isEmpty() ) {
168 if ( pattern->op() == KMSearchPattern::OpOr && parts.size() > 1 ) {
169 search = "(OR " + parts.join(" ") + ')';
170 } else {
171 // and's are simply joined
172 search = parts.join(" ");
176 kDebug(5006) << search <<";localSearch=" << mLocalSearchPattern->asString();
177 return search;
180 //-----------------------------------------------------------------------------
181 void SearchJob::slotSearchData( KJob* job, const QString& data, const QString& )
183 if ( job && job->error() ) {
184 // error is handled in slotSearchResult
185 return;
188 if ( mLocalSearchPattern->isEmpty() && data.isEmpty() )
190 // no local search and the server found nothing
191 QList<quint32> serNums;
192 emit searchDone( serNums, mSearchPattern, true );
193 } else
195 // remember the uids the server found
196 mImapSearchHits = data.split( ' ', QString::SkipEmptyParts );
198 if ( canMapAllUIDs() )
200 slotSearchFolder();
201 } else
203 // get the folder to make sure we have all messages
204 connect ( mFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
205 this, SLOT( slotSearchFolder()) );
206 mFolder->getFolder();
211 //-----------------------------------------------------------------------------
212 bool SearchJob::canMapAllUIDs()
214 for ( QStringList::Iterator it = mImapSearchHits.begin();
215 it != mImapSearchHits.end(); ++it )
217 if ( mFolder->serNumForUID( (*it).toULong() ) == 0 )
218 return false;
220 return true;
223 //-----------------------------------------------------------------------------
224 void SearchJob::slotSearchFolder()
226 disconnect ( mFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
227 this, SLOT( slotSearchFolder()) );
229 if ( mLocalSearchPattern->isEmpty() ) {
230 // pure imap search - now get the serial number for the UIDs
231 QList<quint32> serNums;
232 for ( QStringList::Iterator it = mImapSearchHits.begin();
233 it != mImapSearchHits.end(); ++it ) {
234 ulong serNum = mFolder->serNumForUID( (*it).toULong() );
235 // Check that the local folder does contain a message for this UID.
236 // Scenario: server responds with a list of UIDs.
237 // While the search was running, filtering or bad juju moved a message
238 // locally serNumForUID will happily return 0 for the missing message,
239 // and KMFolderSearch::addSerNum() will fail its assertion.
240 if ( serNum != 0 ) {
241 serNums.append( serNum );
244 emit searchDone( serNums, mSearchPattern, true );
245 } else {
246 // we have search patterns that can not be handled by the server
247 mRemainingMsgs = mFolder->count();
248 if ( mRemainingMsgs == 0 ) {
249 emit searchDone( mSearchSerNums, mSearchPattern, true );
250 return;
253 // Let's see if all we need is status, that we can do locally. Optimization.
254 bool needToDownload = needsDownload();
255 if ( needToDownload ) {
256 // so we need to download all messages and check
257 QString question = i18n("To execute your search all messages of the folder %1 "
258 "have to be downloaded from the server. This may take some time. "
259 "Do you want to continue your search?", mFolder->label() );
260 if ( KMessageBox::warningContinueCancel( 0, question
261 , i18n("Continue Search"), KGuiItem( i18nc( "Continue search button.", "&Search") )
262 , KStandardGuiItem::cancel(), "continuedownloadingforsearch" )
263 != KMessageBox::Continue )
265 QList<quint32> serNums;
266 emit searchDone( serNums, mSearchPattern, true );
267 return;
270 unsigned int numMsgs = mRemainingMsgs;
271 // progress
272 mProgress = ProgressManager::createProgressItem(
273 "ImapSearchDownload" + ProgressManager::getUniqueID(),
274 i18n("Downloading emails from IMAP server"),
275 i18n( "URL: %1", Qt::escape( mFolder->folder()->prettyUrl() ) ),
276 true,
277 mAccount->useSSL() || mAccount->useTLS() );
278 mProgress->setTotalItems( numMsgs );
279 connect ( mProgress, SIGNAL( progressItemCanceled( KPIM::ProgressItem*)),
280 this, SLOT( slotAbortSearch( KPIM::ProgressItem* ) ) );
282 for ( unsigned int i = 0; i < numMsgs ; ++i ) {
283 KMMessage * msg = mFolder->getMsg( i );
284 if ( needToDownload ) {
285 ImapJob *job = new ImapJob( msg );
286 job->setParentFolder( mFolder );
287 job->setParentProgressItem( mProgress );
288 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
289 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
290 job->start();
291 } else {
292 slotSearchMessageArrived( msg );
298 //-----------------------------------------------------------------------------
299 void SearchJob::slotSearchMessageArrived( KMMessage* msg )
301 if ( mProgress )
303 mProgress->incCompletedItems();
304 mProgress->updateProgress();
306 --mRemainingMsgs;
307 bool matches = false;
308 if ( msg ) { // messageRetrieved(0) is always possible
309 if ( mLocalSearchPattern->op() == KMSearchPattern::OpAnd ) {
310 // imap and local search have to match
311 if ( mLocalSearchPattern->matches( msg ) &&
312 ( mImapSearchHits.isEmpty() ||
313 mImapSearchHits.contains( QString::number(msg->UID() ) ) ) ) {
314 quint32 serNum = msg->getMsgSerNum();
315 mSearchSerNums.append( serNum );
316 matches = true;
318 } else if ( mLocalSearchPattern->op() == KMSearchPattern::OpOr ) {
319 // imap or local search have to match
320 if ( mLocalSearchPattern->matches( msg ) ||
321 mImapSearchHits.contains( QString::number(msg->UID()) ) ) {
322 quint32 serNum = msg->getMsgSerNum();
323 mSearchSerNums.append( serNum );
324 matches = true;
327 int idx = -1;
328 KMFolder * p = 0;
329 KMMsgDict::instance()->getLocation( msg, &p, &idx );
330 if ( idx != -1 && mUngetCurrentMsg )
331 mFolder->unGetMsg( idx );
333 if ( mSerNum > 0 )
335 emit searchDone( mSerNum, mSearchPattern, matches );
336 } else {
337 bool complete = ( mRemainingMsgs == 0 );
338 if ( complete && mProgress )
340 mProgress->setComplete();
341 mProgress = 0;
343 if ( matches || complete )
345 emit searchDone( mSearchSerNums, mSearchPattern, complete );
346 mSearchSerNums.clear();
351 //-----------------------------------------------------------------------------
352 void SearchJob::slotSearchResult( KJob *job )
354 if ( job->error() )
356 mAccount->handleJobError( static_cast<KIO::Job*>(job), i18n("Error while searching.") );
357 if ( mSerNum == 0 )
359 // folder
360 QList<quint32> serNums;
361 emit searchDone( serNums, mSearchPattern, true );
362 } else {
363 // message
364 emit searchDone( mSerNum, mSearchPattern, false );
369 //-----------------------------------------------------------------------------
370 void SearchJob::searchSingleMessage()
372 QString searchString = searchStringFromPattern( mSearchPattern );
373 if ( searchString.isEmpty() )
375 // no imap search
376 slotSearchDataSingleMessage( 0, QString(), QString() );
377 } else
379 // imap search
380 int idx = -1;
381 KMFolder *aFolder = 0;
382 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
383 assert(aFolder && (idx != -1));
384 KMMsgBase *mb = mFolder->getMsgBase( idx );
386 // only search for that UID
387 searchString += " UID " + QString::number( mb->UID() );
388 KUrl url = mAccount->getUrl();
389 url.setPath( mFolder->imapPath() + ";SECTION=" + searchString );
390 QByteArray packedArgs;
391 QDataStream stream( &packedArgs, QIODevice::WriteOnly );
392 stream << (int) 'E' << url;
393 KIO::SimpleJob *job = KIO::special( url, packedArgs, KIO::HideProgressInfo );
394 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
395 connect( job, SIGNAL(infoMessage(KJob*,const QString&,const QString&)),
396 SLOT(slotSearchDataSingleMessage(KJob*,const QString&,const QString&)) );
397 connect( job, SIGNAL(result(KJob *)),
398 SLOT(slotSearchResult(KJob *)) );
402 //-----------------------------------------------------------------------------
403 void SearchJob::slotSearchDataSingleMessage( KJob* job, const QString& data,const QString& )
405 if ( job && job->error() ) {
406 // error is handled in slotSearchResult
407 return;
410 if ( mLocalSearchPattern->isEmpty() ) {
411 // we are done
412 emit searchDone( mSerNum, mSearchPattern, !data.isEmpty() );
413 return;
415 // remember what the server found
416 mImapSearchHits = data.split( ' ', QString::SkipEmptyParts );
418 // add the local search
419 int idx = -1;
420 KMFolder *aFolder = 0;
421 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
422 assert(aFolder && (idx != -1));
423 mUngetCurrentMsg = !mFolder->getMsgBase( idx )->isMessage();
424 KMMessage * msg = mFolder->getMsg( idx );
425 if ( needsDownload() ) {
426 ImapJob *job = new ImapJob( msg );
427 job->setParentFolder( mFolder );
428 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
429 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
430 job->start();
431 } else {
432 slotSearchMessageArrived( msg );
436 //-----------------------------------------------------------------------------
437 void SearchJob::slotAbortSearch( KPIM::ProgressItem* item )
439 if ( item )
440 item->setComplete();
441 mAccount->killAllJobs();
442 QList<quint32> serNums;
443 emit searchDone( serNums, mSearchPattern, true );
446 //-----------------------------------------------------------------------------
447 bool SearchJob::needsDownload()
449 QList<KMSearchRule*>::const_iterator it;
450 for ( it = mLocalSearchPattern->begin() ;
451 it != mLocalSearchPattern->end() ; ++it ) {
452 if ( (*it)->field() != "<status>" ) {
453 return true;
456 return false;
459 } // namespace KMail
461 #include "searchjob.moc"