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
00114 if ( (*it)->function() == KMSearchRule::FuncContainsNot ) {
00115 result = "NOT ";
00116 } else if ( (*it)->function() == KMSearchRule::FuncIsGreater &&
00117 (*it)->field() == "<size>" ) {
00118 result = "LARGER ";
00119 } else if ( (*it)->function() == KMSearchRule::FuncIsLess &&
00120 (*it)->field() == "<size>" ) {
00121 result = "SMALLER ";
00122 } else if ( (*it)->function() != KMSearchRule::FuncContains ) {
00123
00124 accept = false;
00125 }
00126
00127
00128 if ( (*it)->field() == "<message>" ) {
00129 result += "TEXT \"" + (*it)->contents() + "\"";
00130 } else if ( (*it)->field() == "<body>" ) {
00131 result += "BODY \"" + (*it)->contents() + "\"";
00132 } else if ( (*it)->field() == "<recipients>" ) {
00133 result += " (OR HEADER To \"" + (*it)->contents() + "\" HEADER Cc \"" +
00134 (*it)->contents() + "\" HEADER Bcc \"" + (*it)->contents() + "\")";
00135 } else if ( (*it)->field() == "<size>" ) {
00136 result += (*it)->contents();
00137 } else if ( (*it)->field() == "<age in days>" ||
00138 (*it)->field() == "<status>" ||
00139 (*it)->field() == "<any header>" ) {
00140 accept = false;
00141 } else {
00142 result += "HEADER "+ field + " \"" + (*it)->contents() + "\"";
00143 }
00144
00145 if ( result.isEmpty() ) {
00146 accept = false;
00147 }
00148
00149 if ( accept ) {
00150 parts += result;
00151 } else {
00152 mLocalSearchPattern->append( *it );
00153 }
00154 }
00155
00156 QString search;
00157 if ( !parts.isEmpty() ) {
00158 if ( pattern->op() == KMSearchPattern::OpOr && parts.size() > 1 ) {
00159 search = "(OR " + parts.join(" ") + ")";
00160 } else {
00161
00162 search = parts.join(" ");
00163 }
00164 }
00165
00166 kdDebug(5006) << k_funcinfo << search << ";localSearch=" << mLocalSearchPattern->asString() << endl;
00167 return search;
00168 }
00169
00170
00171 void SearchJob::slotSearchData( KIO::Job* job, const QString& data )
00172 {
00173 if ( job && job->error() ) {
00174
00175 return;
00176 }
00177
00178 if ( mLocalSearchPattern->isEmpty() && data.isEmpty() )
00179 {
00180
00181 QValueList<Q_UINT32> serNums;
00182 emit searchDone( serNums, mSearchPattern, true );
00183 } else
00184 {
00185
00186 mImapSearchHits = QStringList::split( " ", data );
00187
00188 if ( canMapAllUIDs() )
00189 {
00190 slotSearchFolder();
00191 } else
00192 {
00193
00194 connect ( mFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00195 this, SLOT( slotSearchFolder()) );
00196 mFolder->getFolder();
00197 }
00198 }
00199 }
00200
00201
00202 bool SearchJob::canMapAllUIDs()
00203 {
00204 for ( QStringList::Iterator it = mImapSearchHits.begin();
00205 it != mImapSearchHits.end(); ++it )
00206 {
00207 if ( mFolder->serNumForUID( (*it).toULong() ) == 0 )
00208 return false;
00209 }
00210 return true;
00211 }
00212
00213
00214 void SearchJob::slotSearchFolder()
00215 {
00216 disconnect ( mFolder, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00217 this, SLOT( slotSearchFolder()) );
00218
00219 if ( mLocalSearchPattern->isEmpty() ) {
00220
00221 QValueList<Q_UINT32> serNums;
00222 for ( QStringList::Iterator it = mImapSearchHits.begin();
00223 it != mImapSearchHits.end(); ++it )
00224 {
00225 serNums.append( mFolder->serNumForUID( (*it).toULong() ) );
00226 }
00227 emit searchDone( serNums, mSearchPattern, true );
00228 } else {
00229
00230 mRemainingMsgs = mFolder->count();
00231 if ( mRemainingMsgs == 0 ) {
00232 emit searchDone( mSearchSerNums, mSearchPattern, true );
00233 return;
00234 }
00235
00236
00237 bool needToDownload = needsDownload();
00238 if ( needToDownload ) {
00239
00240 QString question = i18n("To execute your search all messages of the folder %1 "
00241 "have to be downloaded from the server. This may take some time. "
00242 "Do you want to continue your search?").arg( mFolder->label() );
00243 if ( KMessageBox::warningContinueCancel( 0, question,
00244 i18n("Continue Search"), i18n("&Search"),
00245 "continuedownloadingforsearch" ) != KMessageBox::Continue )
00246 {
00247 QValueList<Q_UINT32> serNums;
00248 emit searchDone( serNums, mSearchPattern, true );
00249 return;
00250 }
00251 }
00252 unsigned int numMsgs = mRemainingMsgs;
00253
00254 mProgress = ProgressManager::createProgressItem(
00255 "ImapSearchDownload" + ProgressManager::getUniqueID(),
00256 i18n("Downloading emails from IMAP server"),
00257 i18n( "URL: %1" ).arg( QStyleSheet::escape( mFolder->folder()->prettyURL() ) ),
00258 true,
00259 mAccount->useSSL() || mAccount->useTLS() );
00260 mProgress->setTotalItems( numMsgs );
00261 connect ( mProgress, SIGNAL( progressItemCanceled( KPIM::ProgressItem*)),
00262 this, SLOT( slotAbortSearch( KPIM::ProgressItem* ) ) );
00263
00264 for ( unsigned int i = 0; i < numMsgs ; ++i ) {
00265 KMMessage * msg = mFolder->getMsg( i );
00266 if ( needToDownload ) {
00267 ImapJob *job = new ImapJob( msg );
00268 job->setParentFolder( mFolder );
00269 job->setParentProgressItem( mProgress );
00270 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
00271 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
00272 job->start();
00273 } else {
00274 slotSearchMessageArrived( msg );
00275 }
00276 }
00277 }
00278 }
00279
00280
00281 void SearchJob::slotSearchMessageArrived( KMMessage* msg )
00282 {
00283 if ( mProgress )
00284 {
00285 mProgress->incCompletedItems();
00286 mProgress->updateProgress();
00287 }
00288 --mRemainingMsgs;
00289 bool matches = false;
00290 if ( msg ) {
00291 if ( mLocalSearchPattern->op() == KMSearchPattern::OpAnd ) {
00292
00293 if ( mLocalSearchPattern->matches( msg ) &&
00294 ( mImapSearchHits.isEmpty() ||
00295 mImapSearchHits.find( QString::number(msg->UID() ) ) != mImapSearchHits.end() ) ) {
00296 Q_UINT32 serNum = msg->getMsgSerNum();
00297 mSearchSerNums.append( serNum );
00298 matches = true;
00299 }
00300 } else if ( mLocalSearchPattern->op() == KMSearchPattern::OpOr ) {
00301
00302 if ( mLocalSearchPattern->matches( msg ) ||
00303 mImapSearchHits.find( QString::number(msg->UID()) ) != mImapSearchHits.end() ) {
00304 Q_UINT32 serNum = msg->getMsgSerNum();
00305 mSearchSerNums.append( serNum );
00306 matches = true;
00307 }
00308 }
00309 int idx = -1;
00310 KMFolder * p = 0;
00311 KMMsgDict::instance()->getLocation( msg, &p, &idx );
00312 if ( idx != -1 && mUngetCurrentMsg )
00313 mFolder->unGetMsg( idx );
00314 }
00315 if ( mSerNum > 0 )
00316 {
00317 emit searchDone( mSerNum, mSearchPattern, matches );
00318 } else {
00319 bool complete = ( mRemainingMsgs == 0 );
00320 if ( complete && mProgress )
00321 {
00322 mProgress->setComplete();
00323 mProgress = 0;
00324 }
00325 if ( matches || complete )
00326 {
00327 emit searchDone( mSearchSerNums, mSearchPattern, complete );
00328 mSearchSerNums.clear();
00329 }
00330 }
00331 }
00332
00333
00334 void SearchJob::slotSearchResult( KIO::Job *job )
00335 {
00336 if ( job->error() )
00337 {
00338 mAccount->handleJobError( job, i18n("Error while searching.") );
00339 if ( mSerNum == 0 )
00340 {
00341
00342 QValueList<Q_UINT32> serNums;
00343 emit searchDone( serNums, mSearchPattern, true );
00344 } else {
00345
00346 emit searchDone( mSerNum, mSearchPattern, false );
00347 }
00348 }
00349 }
00350
00351
00352 void SearchJob::searchSingleMessage()
00353 {
00354 QString searchString = searchStringFromPattern( mSearchPattern );
00355 if ( searchString.isEmpty() )
00356 {
00357
00358 slotSearchDataSingleMessage( 0, QString::null );
00359 } else
00360 {
00361
00362 int idx = -1;
00363 KMFolder *aFolder = 0;
00364 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
00365 assert(aFolder && (idx != -1));
00366 KMMsgBase *mb = mFolder->getMsgBase( idx );
00367
00368
00369 searchString += " UID " + QString::number( mb->UID() );
00370 KURL url = mAccount->getUrl();
00371 url.setPath( mFolder->imapPath() + ";SECTION=" + searchString );
00372 QByteArray packedArgs;
00373 QDataStream stream( packedArgs, IO_WriteOnly );
00374 stream << (int) 'E' << url;
00375 KIO::SimpleJob *job = KIO::special( url, packedArgs, false );
00376 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00377 connect( job, SIGNAL(infoMessage(KIO::Job*,const QString&)),
00378 SLOT(slotSearchDataSingleMessage(KIO::Job*,const QString&)) );
00379 connect( job, SIGNAL(result(KIO::Job *)),
00380 SLOT(slotSearchResult(KIO::Job *)) );
00381 }
00382 }
00383
00384
00385 void SearchJob::slotSearchDataSingleMessage( KIO::Job* job, const QString& data )
00386 {
00387 if ( job && job->error() ) {
00388
00389 return;
00390 }
00391
00392 if ( mLocalSearchPattern->isEmpty() ) {
00393
00394 emit searchDone( mSerNum, mSearchPattern, !data.isEmpty() );
00395 return;
00396 }
00397
00398 mImapSearchHits = QStringList::split( " ", data );
00399
00400
00401 int idx = -1;
00402 KMFolder *aFolder = 0;
00403 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
00404 assert(aFolder && (idx != -1));
00405 mUngetCurrentMsg = !mFolder->getMsgBase( idx )->isMessage();
00406 KMMessage * msg = mFolder->getMsg( idx );
00407 if ( needsDownload() ) {
00408 ImapJob *job = new ImapJob( msg );
00409 job->setParentFolder( mFolder );
00410 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
00411 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
00412 job->start();
00413 } else {
00414 slotSearchMessageArrived( msg );
00415 }
00416 }
00417
00418
00419 void SearchJob::slotAbortSearch( KPIM::ProgressItem* item )
00420 {
00421 if ( item )
00422 item->setComplete();
00423 mAccount->killAllJobs();
00424 QValueList<Q_UINT32> serNums;
00425 emit searchDone( serNums, mSearchPattern, true );
00426 }
00427
00428
00429 bool SearchJob::needsDownload()
00430 {
00431 for ( QPtrListIterator<KMSearchRule> it( *mLocalSearchPattern ) ; it.current() ; ++it ) {
00432 if ( (*it)->field() != "<status>" ) {
00433 return true;
00434 }
00435 }
00436 return false;
00437 }
00438
00439 }
00440
00441 #include "searchjob.moc"