00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "searchjob.h"
00030 #include "kmfolderimap.h"
00031 #include "imapaccountbase.h"
00032 #include "kmsearchpattern.h"
00033 #include "kmfolder.h"
00034 #include "imapjob.h"
00035 #include "kmmsgdict.h"
00036
00037 #include <progressmanager.h>
00038 using KPIM::ProgressItem;
00039 using KPIM::ProgressManager;
00040
00041 #include <kdebug.h>
00042 #include <kurl.h>
00043 #include <kio/scheduler.h>
00044 #include <kio/job.h>
00045 #include <kio/global.h>
00046 #include <klocale.h>
00047 #include <kmessagebox.h>
00048
00049 #include <qstylesheet.h>
00050
00051 namespace KMail {
00052
00053 SearchJob::SearchJob( KMFolderImap* folder, ImapAccountBase* account,
00054 const KMSearchPattern* pattern, Q_UINT32 serNum )
00055 : FolderJob( 0, tOther, (folder ? folder->folder() : 0) ),
00056 mFolder( folder ), mAccount( account ), mSearchPattern( pattern ),
00057 mSerNum( serNum ), mRemainingMsgs( 0 ), mProgress( 0 ),
00058 mUngetCurrentMsg( false )
00059 {
00060 }
00061
00062 SearchJob::~SearchJob()
00063 {
00064 }
00065
00066 void SearchJob::execute()
00067 {
00068 if ( mSerNum == 0 )
00069 {
00070 searchCompleteFolder();
00071 } else {
00072 searchSingleMessage();
00073 }
00074 }
00075
00076
00077 void SearchJob::searchCompleteFolder()
00078 {
00079
00080 QString searchString = searchStringFromPattern( mSearchPattern );
00081
00082 if ( searchString.isEmpty() )
00083 return slotSearchData( 0, QString::null );
00084
00085
00086 KURL url = mAccount->getUrl();
00087 url.setPath( mFolder->imapPath() + ";SECTION=" + searchString );
00088 QByteArray packedArgs;
00089 QDataStream stream( packedArgs, IO_WriteOnly );
00090 stream << (int) 'E' << url;
00091 KIO::SimpleJob *job = KIO::special( url, packedArgs, false );
00092 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00093 connect( job, SIGNAL(infoMessage(KIO::Job*,const QString&)),
00094 SLOT(slotSearchData(KIO::Job*,const QString&)) );
00095 connect( job, SIGNAL(result(KIO::Job *)),
00096 SLOT(slotSearchResult(KIO::Job *)) );
00097 }
00098
00099
00100 QString SearchJob::searchStringFromPattern( const KMSearchPattern* pattern )
00101 {
00102 QStringList parts;
00103
00104 mLocalSearchPattern = new KMSearchPattern();
00105 mLocalSearchPattern->setOp( pattern->op() );
00106
00107 for ( QPtrListIterator<KMSearchRule> it( *pattern ) ; it.current() ; ++it )
00108 {
00109
00110 bool accept = true;
00111 QString result;
00112 QString field = (*it)->field();
00113 result = "CHARSET UTF-8 ";
00114
00115 if ( (*it)->function() == KMSearchRule::FuncContainsNot ) {
00116 result += "NOT ";
00117 } else if ( (*it)->function() == KMSearchRule::FuncIsGreater &&
00118 (*it)->field() == "<size>" ) {
00119 result += "LARGER ";
00120 } else if ( (*it)->function() == KMSearchRule::FuncIsLess &&
00121 (*it)->field() == "<size>" ) {
00122 result += "SMALLER ";
00123 } else if ( (*it)->function() != KMSearchRule::FuncContains ) {
00124
00125 accept = false;
00126 }
00127
00128
00129 if ( (*it)->field() == "<message>" ) {
00130 result += "TEXT \"" + (*it)->contents() + "\"";
00131 } else if ( (*it)->field() == "<body>" ) {
00132 result += "BODY \"" + (*it)->contents() + "\"";
00133 } else if ( (*it)->field() == "<recipients>" ) {
00134 result += " (OR HEADER To \"" + (*it)->contents() + "\" HEADER Cc \"" +
00135 (*it)->contents() + "\" HEADER Bcc \"" + (*it)->contents() + "\")";
00136 } else if ( (*it)->field() == "<size>" ) {
00137 result += (*it)->contents();
00138 } else if ( (*it)->field() == "<age in days>" ||
00139 (*it)->field() == "<status>" ||
00140 (*it)->field() == "<any header>" ) {
00141 accept = false;
00142 } else {
00143 result += "HEADER "+ field + " \"" + (*it)->contents() + "\"";
00144 }
00145
00146 if ( result.isEmpty() ) {
00147 accept = false;
00148 }
00149
00150 if ( accept ) {
00151 parts += result;
00152 } else {
00153 mLocalSearchPattern->append( *it );
00154 }
00155 }
00156
00157 QString search;
00158 if ( !parts.isEmpty() ) {
00159 if ( pattern->op() == KMSearchPattern::OpOr && parts.size() > 1 ) {
00160 search = "(OR " + parts.join(" ") + ")";
00161 } else {
00162
00163 search = parts.join(" ");
00164 }
00165 }
00166
00167 kdDebug(5006) << k_funcinfo << search << ";localSearch=" << mLocalSearchPattern->asString() << endl;
00168 return search;
00169 }
00170
00171
00172 void SearchJob::slotSearchData( KIO::Job* job, const QString& data )
00173 {
00174 if ( job && job->error() ) {
00175
00176 return;
00177 }
00178
00179 if ( mLocalSearchPattern->isEmpty() && data.isEmpty() )
00180 {
00181
00182 QValueList<Q_UINT32> serNums;
00183 emit searchDone( serNums, mSearchPattern, true );
00184 } else
00185 {
00186
00187 mImapSearchHits = QStringList::split( " ", data );
00188
00189 if ( canMapAllUIDs() )
00190 {
00191 slotSearchFolder();
00192 } else
00193 {
00194
00195 connect ( mFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00196 this, SLOT( slotSearchFolder()) );
00197 mFolder->getFolder();
00198 }
00199 }
00200 }
00201
00202
00203 bool SearchJob::canMapAllUIDs()
00204 {
00205 for ( QStringList::Iterator it = mImapSearchHits.begin();
00206 it != mImapSearchHits.end(); ++it )
00207 {
00208 if ( mFolder->serNumForUID( (*it).toULong() ) == 0 )
00209 return false;
00210 }
00211 return true;
00212 }
00213
00214
00215 void SearchJob::slotSearchFolder()
00216 {
00217 disconnect ( mFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00218 this, SLOT( slotSearchFolder()) );
00219
00220 if ( mLocalSearchPattern->isEmpty() ) {
00221
00222 QValueList<Q_UINT32> serNums;
00223 for ( QStringList::Iterator it = mImapSearchHits.begin();
00224 it != mImapSearchHits.end(); ++it )
00225 {
00226 ulong serNum = mFolder->serNumForUID( (*it).toULong() );
00227
00228
00229
00230 if ( serNum != 0 )
00231 serNums.append( serNum );
00232 }
00233 emit searchDone( serNums, mSearchPattern, true );
00234 } else {
00235
00236 mRemainingMsgs = mFolder->count();
00237 if ( mRemainingMsgs == 0 ) {
00238 emit searchDone( mSearchSerNums, mSearchPattern, true );
00239 return;
00240 }
00241
00242
00243 bool needToDownload = needsDownload();
00244 if ( needToDownload ) {
00245
00246 QString question = i18n("To execute your search all messages of the folder %1 "
00247 "have to be downloaded from the server. This may take some time. "
00248 "Do you want to continue your search?").arg( mFolder->label() );
00249 if ( KMessageBox::warningContinueCancel( 0, question,
00250 i18n("Continue Search"), i18n("&Search"),
00251 "continuedownloadingforsearch" ) != KMessageBox::Continue )
00252 {
00253 QValueList<Q_UINT32> serNums;
00254 emit searchDone( serNums, mSearchPattern, true );
00255 return;
00256 }
00257 }
00258 unsigned int numMsgs = mRemainingMsgs;
00259
00260 mProgress = ProgressManager::createProgressItem(
00261 "ImapSearchDownload" + ProgressManager::getUniqueID(),
00262 i18n("Downloading emails from IMAP server"),
00263 i18n( "URL: %1" ).arg( QStyleSheet::escape( mFolder->folder()->prettyURL() ) ),
00264 true,
00265 mAccount->useSSL() || mAccount->useTLS() );
00266 mProgress->setTotalItems( numMsgs );
00267 connect ( mProgress, SIGNAL( progressItemCanceled( KPIM::ProgressItem*)),
00268 this, SLOT( slotAbortSearch( KPIM::ProgressItem* ) ) );
00269
00270 for ( unsigned int i = 0; i < numMsgs ; ++i ) {
00271 KMMessage * msg = mFolder->getMsg( i );
00272 if ( needToDownload ) {
00273 ImapJob *job = new ImapJob( msg );
00274 job->setParentFolder( mFolder );
00275 job->setParentProgressItem( mProgress );
00276 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
00277 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
00278 job->start();
00279 } else {
00280 slotSearchMessageArrived( msg );
00281 }
00282 }
00283 }
00284 }
00285
00286
00287 void SearchJob::slotSearchMessageArrived( KMMessage* msg )
00288 {
00289 if ( mProgress )
00290 {
00291 mProgress->incCompletedItems();
00292 mProgress->updateProgress();
00293 }
00294 --mRemainingMsgs;
00295 bool matches = false;
00296 if ( msg ) {
00297 if ( mLocalSearchPattern->op() == KMSearchPattern::OpAnd ) {
00298
00299 if ( mLocalSearchPattern->matches( msg ) &&
00300 ( mImapSearchHits.isEmpty() ||
00301 mImapSearchHits.find( QString::number(msg->UID() ) ) != mImapSearchHits.end() ) ) {
00302 Q_UINT32 serNum = msg->getMsgSerNum();
00303 mSearchSerNums.append( serNum );
00304 matches = true;
00305 }
00306 } else if ( mLocalSearchPattern->op() == KMSearchPattern::OpOr ) {
00307
00308 if ( mLocalSearchPattern->matches( msg ) ||
00309 mImapSearchHits.find( QString::number(msg->UID()) ) != mImapSearchHits.end() ) {
00310 Q_UINT32 serNum = msg->getMsgSerNum();
00311 mSearchSerNums.append( serNum );
00312 matches = true;
00313 }
00314 }
00315 int idx = -1;
00316 KMFolder * p = 0;
00317 KMMsgDict::instance()->getLocation( msg, &p, &idx );
00318 if ( idx != -1 && mUngetCurrentMsg )
00319 mFolder->unGetMsg( idx );
00320 }
00321 if ( mSerNum > 0 )
00322 {
00323 emit searchDone( mSerNum, mSearchPattern, matches );
00324 } else {
00325 bool complete = ( mRemainingMsgs == 0 );
00326 if ( complete && mProgress )
00327 {
00328 mProgress->setComplete();
00329 mProgress = 0;
00330 }
00331 if ( matches || complete )
00332 {
00333 emit searchDone( mSearchSerNums, mSearchPattern, complete );
00334 mSearchSerNums.clear();
00335 }
00336 }
00337 }
00338
00339
00340 void SearchJob::slotSearchResult( KIO::Job *job )
00341 {
00342 if ( job->error() )
00343 {
00344 mAccount->handleJobError( job, i18n("Error while searching.") );
00345 if ( mSerNum == 0 )
00346 {
00347
00348 QValueList<Q_UINT32> serNums;
00349 emit searchDone( serNums, mSearchPattern, true );
00350 } else {
00351
00352 emit searchDone( mSerNum, mSearchPattern, false );
00353 }
00354 }
00355 }
00356
00357
00358 void SearchJob::searchSingleMessage()
00359 {
00360 QString searchString = searchStringFromPattern( mSearchPattern );
00361 if ( searchString.isEmpty() )
00362 {
00363
00364 slotSearchDataSingleMessage( 0, QString::null );
00365 } else
00366 {
00367
00368 int idx = -1;
00369 KMFolder *aFolder = 0;
00370 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
00371 assert(aFolder && (idx != -1));
00372 KMMsgBase *mb = mFolder->getMsgBase( idx );
00373
00374
00375 searchString += " UID " + QString::number( mb->UID() );
00376 KURL url = mAccount->getUrl();
00377 url.setPath( mFolder->imapPath() + ";SECTION=" + searchString );
00378 QByteArray packedArgs;
00379 QDataStream stream( packedArgs, IO_WriteOnly );
00380 stream << (int) 'E' << url;
00381 KIO::SimpleJob *job = KIO::special( url, packedArgs, false );
00382 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00383 connect( job, SIGNAL(infoMessage(KIO::Job*,const QString&)),
00384 SLOT(slotSearchDataSingleMessage(KIO::Job*,const QString&)) );
00385 connect( job, SIGNAL(result(KIO::Job *)),
00386 SLOT(slotSearchResult(KIO::Job *)) );
00387 }
00388 }
00389
00390
00391 void SearchJob::slotSearchDataSingleMessage( KIO::Job* job, const QString& data )
00392 {
00393 if ( job && job->error() ) {
00394
00395 return;
00396 }
00397
00398 if ( mLocalSearchPattern->isEmpty() ) {
00399
00400 emit searchDone( mSerNum, mSearchPattern, !data.isEmpty() );
00401 return;
00402 }
00403
00404 mImapSearchHits = QStringList::split( " ", data );
00405
00406
00407 int idx = -1;
00408 KMFolder *aFolder = 0;
00409 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
00410 assert(aFolder && (idx != -1));
00411 mUngetCurrentMsg = !mFolder->getMsgBase( idx )->isMessage();
00412 KMMessage * msg = mFolder->getMsg( idx );
00413 if ( needsDownload() ) {
00414 ImapJob *job = new ImapJob( msg );
00415 job->setParentFolder( mFolder );
00416 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
00417 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
00418 job->start();
00419 } else {
00420 slotSearchMessageArrived( msg );
00421 }
00422 }
00423
00424
00425 void SearchJob::slotAbortSearch( KPIM::ProgressItem* item )
00426 {
00427 if ( item )
00428 item->setComplete();
00429 mAccount->killAllJobs();
00430 QValueList<Q_UINT32> serNums;
00431 emit searchDone( serNums, mSearchPattern, true );
00432 }
00433
00434
00435 bool SearchJob::needsDownload()
00436 {
00437 for ( QPtrListIterator<KMSearchRule> it( *mLocalSearchPattern ) ; it.current() ; ++it ) {
00438 if ( (*it)->field() != "<status>" ) {
00439 return true;
00440 }
00441 }
00442 return false;
00443 }
00444
00445 }
00446
00447 #include "searchjob.moc"