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 ulong serNum = mFolder->serNumForUID( (*it).toULong() );
00226
00227
00228
00229 if ( serNum != 0 )
00230 serNums.append( serNum );
00231 }
00232 emit searchDone( serNums, mSearchPattern, true );
00233 } else {
00234
00235 mRemainingMsgs = mFolder->count();
00236 if ( mRemainingMsgs == 0 ) {
00237 emit searchDone( mSearchSerNums, mSearchPattern, true );
00238 return;
00239 }
00240
00241
00242 bool needToDownload = needsDownload();
00243 if ( needToDownload ) {
00244
00245 QString question = i18n("To execute your search all messages of the folder %1 "
00246 "have to be downloaded from the server. This may take some time. "
00247 "Do you want to continue your search?").arg( mFolder->label() );
00248 if ( KMessageBox::warningContinueCancel( 0, question,
00249 i18n("Continue Search"), i18n("&Search"),
00250 "continuedownloadingforsearch" ) != KMessageBox::Continue )
00251 {
00252 QValueList<Q_UINT32> serNums;
00253 emit searchDone( serNums, mSearchPattern, true );
00254 return;
00255 }
00256 }
00257 unsigned int numMsgs = mRemainingMsgs;
00258
00259 mProgress = ProgressManager::createProgressItem(
00260 "ImapSearchDownload" + ProgressManager::getUniqueID(),
00261 i18n("Downloading emails from IMAP server"),
00262 i18n( "URL: %1" ).arg( QStyleSheet::escape( mFolder->folder()->prettyURL() ) ),
00263 true,
00264 mAccount->useSSL() || mAccount->useTLS() );
00265 mProgress->setTotalItems( numMsgs );
00266 connect ( mProgress, SIGNAL( progressItemCanceled( KPIM::ProgressItem*)),
00267 this, SLOT( slotAbortSearch( KPIM::ProgressItem* ) ) );
00268
00269 for ( unsigned int i = 0; i < numMsgs ; ++i ) {
00270 KMMessage * msg = mFolder->getMsg( i );
00271 if ( needToDownload ) {
00272 ImapJob *job = new ImapJob( msg );
00273 job->setParentFolder( mFolder );
00274 job->setParentProgressItem( mProgress );
00275 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
00276 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
00277 job->start();
00278 } else {
00279 slotSearchMessageArrived( msg );
00280 }
00281 }
00282 }
00283 }
00284
00285
00286 void SearchJob::slotSearchMessageArrived( KMMessage* msg )
00287 {
00288 if ( mProgress )
00289 {
00290 mProgress->incCompletedItems();
00291 mProgress->updateProgress();
00292 }
00293 --mRemainingMsgs;
00294 bool matches = false;
00295 if ( msg ) {
00296 if ( mLocalSearchPattern->op() == KMSearchPattern::OpAnd ) {
00297
00298 if ( mLocalSearchPattern->matches( msg ) &&
00299 ( mImapSearchHits.isEmpty() ||
00300 mImapSearchHits.find( QString::number(msg->UID() ) ) != mImapSearchHits.end() ) ) {
00301 Q_UINT32 serNum = msg->getMsgSerNum();
00302 mSearchSerNums.append( serNum );
00303 matches = true;
00304 }
00305 } else if ( mLocalSearchPattern->op() == KMSearchPattern::OpOr ) {
00306
00307 if ( mLocalSearchPattern->matches( msg ) ||
00308 mImapSearchHits.find( QString::number(msg->UID()) ) != mImapSearchHits.end() ) {
00309 Q_UINT32 serNum = msg->getMsgSerNum();
00310 mSearchSerNums.append( serNum );
00311 matches = true;
00312 }
00313 }
00314 int idx = -1;
00315 KMFolder * p = 0;
00316 KMMsgDict::instance()->getLocation( msg, &p, &idx );
00317 if ( idx != -1 && mUngetCurrentMsg )
00318 mFolder->unGetMsg( idx );
00319 }
00320 if ( mSerNum > 0 )
00321 {
00322 emit searchDone( mSerNum, mSearchPattern, matches );
00323 } else {
00324 bool complete = ( mRemainingMsgs == 0 );
00325 if ( complete && mProgress )
00326 {
00327 mProgress->setComplete();
00328 mProgress = 0;
00329 }
00330 if ( matches || complete )
00331 {
00332 emit searchDone( mSearchSerNums, mSearchPattern, complete );
00333 mSearchSerNums.clear();
00334 }
00335 }
00336 }
00337
00338
00339 void SearchJob::slotSearchResult( KIO::Job *job )
00340 {
00341 if ( job->error() )
00342 {
00343 mAccount->handleJobError( job, i18n("Error while searching.") );
00344 if ( mSerNum == 0 )
00345 {
00346
00347 QValueList<Q_UINT32> serNums;
00348 emit searchDone( serNums, mSearchPattern, true );
00349 } else {
00350
00351 emit searchDone( mSerNum, mSearchPattern, false );
00352 }
00353 }
00354 }
00355
00356
00357 void SearchJob::searchSingleMessage()
00358 {
00359 QString searchString = searchStringFromPattern( mSearchPattern );
00360 if ( searchString.isEmpty() )
00361 {
00362
00363 slotSearchDataSingleMessage( 0, QString::null );
00364 } else
00365 {
00366
00367 int idx = -1;
00368 KMFolder *aFolder = 0;
00369 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
00370 assert(aFolder && (idx != -1));
00371 KMMsgBase *mb = mFolder->getMsgBase( idx );
00372
00373
00374 searchString += " UID " + QString::number( mb->UID() );
00375 KURL url = mAccount->getUrl();
00376 url.setPath( mFolder->imapPath() + ";SECTION=" + searchString );
00377 QByteArray packedArgs;
00378 QDataStream stream( packedArgs, IO_WriteOnly );
00379 stream << (int) 'E' << url;
00380 KIO::SimpleJob *job = KIO::special( url, packedArgs, false );
00381 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00382 connect( job, SIGNAL(infoMessage(KIO::Job*,const QString&)),
00383 SLOT(slotSearchDataSingleMessage(KIO::Job*,const QString&)) );
00384 connect( job, SIGNAL(result(KIO::Job *)),
00385 SLOT(slotSearchResult(KIO::Job *)) );
00386 }
00387 }
00388
00389
00390 void SearchJob::slotSearchDataSingleMessage( KIO::Job* job, const QString& data )
00391 {
00392 if ( job && job->error() ) {
00393
00394 return;
00395 }
00396
00397 if ( mLocalSearchPattern->isEmpty() ) {
00398
00399 emit searchDone( mSerNum, mSearchPattern, !data.isEmpty() );
00400 return;
00401 }
00402
00403 mImapSearchHits = QStringList::split( " ", data );
00404
00405
00406 int idx = -1;
00407 KMFolder *aFolder = 0;
00408 KMMsgDict::instance()->getLocation( mSerNum, &aFolder, &idx );
00409 assert(aFolder && (idx != -1));
00410 mUngetCurrentMsg = !mFolder->getMsgBase( idx )->isMessage();
00411 KMMessage * msg = mFolder->getMsg( idx );
00412 if ( needsDownload() ) {
00413 ImapJob *job = new ImapJob( msg );
00414 job->setParentFolder( mFolder );
00415 connect( job, SIGNAL(messageRetrieved(KMMessage*)),
00416 this, SLOT(slotSearchMessageArrived(KMMessage*)) );
00417 job->start();
00418 } else {
00419 slotSearchMessageArrived( msg );
00420 }
00421 }
00422
00423
00424 void SearchJob::slotAbortSearch( KPIM::ProgressItem* item )
00425 {
00426 if ( item )
00427 item->setComplete();
00428 mAccount->killAllJobs();
00429 QValueList<Q_UINT32> serNums;
00430 emit searchDone( serNums, mSearchPattern, true );
00431 }
00432
00433
00434 bool SearchJob::needsDownload()
00435 {
00436 for ( QPtrListIterator<KMSearchRule> it( *mLocalSearchPattern ) ; it.current() ; ++it ) {
00437 if ( (*it)->field() != "<status>" ) {
00438 return true;
00439 }
00440 }
00441 return false;
00442 }
00443
00444 }
00445
00446 #include "searchjob.moc"