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