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