kmail Library API Documentation

kmfoldersearch.cpp

00001 // Author: Don Sanders <sanders@kde.org> 00002 // License GPL 00003 00004 //Factor byteswap stuff into one header file 00005 00006 #include "kmfoldersearch.h" 00007 #include "kmfolderimap.h" 00008 #include "kmfoldermgr.h" 00009 #include "kmsearchpattern.h" 00010 #include "kmmsgdict.h" 00011 #include "kmmsgindex.h" 00012 #include "jobscheduler.h" 00013 00014 #include <kdebug.h> 00015 #include <klocale.h> 00016 #include <kconfig.h> 00017 00018 #include <qfileinfo.h> 00019 00020 #include <assert.h> 00021 #include <stdio.h> 00022 #include <unistd.h> 00023 #include <errno.h> 00024 #include <stdlib.h> 00025 #include <sys/types.h> 00026 #include <sys/stat.h> 00027 #include <sys/file.h> 00028 #include <utime.h> 00029 #include <config.h> 00030 00031 #ifdef HAVE_BYTESWAP_H 00032 #include <byteswap.h> 00033 #endif 00034 00035 // We define functions as kmail_swap_NN so that we don't get compile errors 00036 // on platforms where bswap_NN happens to be a function instead of a define. 00037 00038 /* Swap bytes in 32 bit value. */ 00039 #ifdef bswap_32 00040 #define kmail_swap_32(x) bswap_32(x) 00041 #else 00042 #define kmail_swap_32(x) \ 00043 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ 00044 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) 00045 #endif 00046 00047 // Current version of the .index.search files 00048 #define IDS_VERSION 1000 00049 // The asterisk at the end is important 00050 #define IDS_HEADER "# KMail-Search-IDs V%d\n*" 00051 #define IDS_HEADER_LEN 30 00052 00053 00054 KMSearch::KMSearch(QObject * parent, const char * name) 00055 :QObject(parent, name) 00056 { 00057 mRemainingFolders = -1; 00058 mRemainingMessages = -1; 00059 mRecursive = true; 00060 mRunByIndex = mRunning = false; 00061 mIdle = false; 00062 mRoot = 0; 00063 mSearchPattern = 0; 00064 mSearchedCount = 0; 00065 mFoundCount = 0; 00066 mProcessNextBatchTimer = new QTimer(); 00067 connect(mProcessNextBatchTimer, SIGNAL(timeout()), 00068 this, SLOT(slotProcessNextBatch())); 00069 } 00070 00071 KMSearch::~KMSearch() 00072 { 00073 delete mProcessNextBatchTimer; 00074 delete mSearchPattern; 00075 } 00076 00077 bool KMSearch::write(QString location) const 00078 { 00079 KConfig config(location); 00080 config.setGroup("Search Folder"); 00081 if (mSearchPattern) 00082 mSearchPattern->writeConfig(&config); 00083 if (mRoot.isNull()) 00084 config.writeEntry("Base", ""); 00085 else 00086 config.writeEntry("Base", mRoot->idString()); 00087 config.writeEntry("Recursive", recursive()); 00088 return true; 00089 } 00090 00091 bool KMSearch::read(QString location) 00092 { 00093 KConfig config(location); 00094 config.setGroup("Search Folder"); 00095 if (!mSearchPattern) 00096 mSearchPattern = new KMSearchPattern(); 00097 mSearchPattern->readConfig(&config); 00098 QString rootString = config.readEntry("Base"); 00099 mRoot = kmkernel->findFolderById(rootString); 00100 mRecursive = config.readBoolEntry("Recursive"); 00101 return true; 00102 } 00103 00104 void KMSearch::setSearchPattern(KMSearchPattern *searchPattern) 00105 { 00106 if (running()) 00107 stop(); 00108 if (mSearchPattern != searchPattern) { 00109 delete mSearchPattern; 00110 mSearchPattern = searchPattern; 00111 } 00112 } 00113 00114 bool KMSearch::inScope(KMFolder* folder) const 00115 { 00116 if (mRoot.isNull() || folder == mRoot) 00117 return true; 00118 if (!recursive()) 00119 return false; 00120 00121 KMFolderDir *rootDir = mRoot->child(); 00122 KMFolderDir *ancestorDir = folder->parent(); 00123 while (ancestorDir) { 00124 if (ancestorDir == rootDir) 00125 return true; 00126 ancestorDir = ancestorDir->parent(); 00127 } 00128 return false; 00129 } 00130 00131 void KMSearch::start() 00132 { 00133 if (running()) 00134 return; 00135 00136 if (!mSearchPattern) { 00137 emit finished(true); 00138 return; 00139 } 00140 00141 mSearchedCount = 0; 00142 mFoundCount = 0; 00143 mRunning = true; 00144 mRunByIndex = false; 00145 if(kmkernel->msgIndex() && kmkernel->msgIndex()->startQuery(this)) { 00146 mRunByIndex = true; 00147 return; 00148 } 00149 00150 QValueList<QGuardedPtr<KMFolder> > folders; 00151 folders.append(mRoot); 00152 if (recursive()) { //Append all descendants to folders 00153 KMFolderNode* node; 00154 KMFolder* folder; 00155 QValueListConstIterator<QGuardedPtr<KMFolder> > it; 00156 for (it = folders.begin(); it != folders.end(); ++it) { 00157 folder = *it; 00158 KMFolderDir *dir = 0; 00159 if (folder) 00160 dir = folder->child(); 00161 else 00162 dir = &kmkernel->folderMgr()->dir(); 00163 if (!dir) 00164 continue; 00165 QPtrListIterator<KMFolderNode> it(*dir); 00166 while ((node = it.current())) { 00167 ++it; 00168 if (!node->isDir()) 00169 { 00170 KMFolder* kmf = dynamic_cast<KMFolder*>(node); 00171 if (kmf) 00172 folders.append(kmf); 00173 } 00174 } 00175 } 00176 } 00177 00178 mLastFolder = QString::null; 00179 mRemainingFolders = folders.count(); 00180 mRemainingMessages = 0; 00181 QValueListConstIterator<QGuardedPtr<KMFolder> > it; 00182 for (it = folders.begin(); it != folders.end(); ++it) { 00183 KMFolder *folder = *it; 00184 if (!folder) { 00185 --mRemainingFolders; 00186 continue; 00187 } 00188 //TODO: Get rid of this protocol check, need a bool KMFolder::isComplete() 00189 if (folder->folderType() == KMFolderTypeImap) { 00190 KMFolderImap *imapFolder = 00191 dynamic_cast<KMFolderImap*>( folder->storage() ); 00192 if (imapFolder && imapFolder->getContentState() == 00193 KMFolderImap::imapNoInformation) { 00194 mIncompleteFolders.append(imapFolder); 00195 connect(imapFolder, SIGNAL(folderComplete(KMFolderImap*, bool)), 00196 SLOT(slotFolderComplete(KMFolderImap*, bool))); 00197 imapFolder->getFolder(); 00198 } else { 00199 mFolders.append(folder); 00200 } 00201 } else { 00202 mFolders.append(folder); 00203 } 00204 } 00205 00206 mProcessNextBatchTimer->start(0, true); 00207 } 00208 00209 void KMSearch::stop() 00210 { 00211 if (!running()) 00212 return; 00213 if(mRunByIndex) { 00214 if(kmkernel->msgIndex()) 00215 kmkernel->msgIndex()->stopQuery(this); 00216 } else { 00217 //kill all pending jobs 00218 QValueListConstIterator<QGuardedPtr<KMFolderImap> > it; 00219 for (it = mIncompleteFolders.begin(); 00220 it != mIncompleteFolders.end(); ++it) { 00221 KMFolderImap *aFolder = (*it); 00222 if (aFolder) 00223 disconnect(aFolder, 00224 SIGNAL(folderComplete(KMFolderImap*, bool)), 00225 this, 00226 SLOT(slotFolderComplete(KMFolderImap*, bool))); 00227 } 00228 mIncompleteFolders.clear(); 00229 QValueListConstIterator<QGuardedPtr<KMFolder> > jt; 00230 for (jt = mOpenedFolders.begin(); jt != mOpenedFolders.end(); ++jt) { 00231 KMFolder *folder = *jt; 00232 if (folder) 00233 folder->close(); 00234 } 00235 } 00236 mOpenedFolders.clear(); 00237 mRemainingMessages = -1; 00238 mRemainingFolders = -1; 00239 mFolders.clear(); 00240 mLastFolder = ""; 00241 mRunByIndex = mRunning = false; 00242 mIdle = false; 00243 emit finished(false); 00244 } 00245 00246 void KMSearch::slotProcessNextBatch() 00247 { 00248 if (!running()) 00249 return; 00250 mIdle = false; 00251 00252 if (mSerNums.count() != 0) { 00253 int i = 10; 00254 QValueListIterator<Q_UINT32> it; 00255 for (it = mSerNums.begin(); it != mSerNums.end();) { 00256 if (--i == 0) 00257 break; 00258 00259 Q_UINT32 serNum = *it; 00260 it = mSerNums.erase(it); 00261 --mRemainingMessages; 00262 ++mSearchedCount; 00263 00264 //TODO: matches should try header rules first and check 00265 // rule->needsBody() and download imap message if needed 00266 if (mSearchPattern && !mSearchPattern->matches(serNum)) 00267 continue; 00268 emit found(serNum); 00269 ++mFoundCount; 00270 } 00271 mProcessNextBatchTimer->start(0, true); 00272 return; 00273 } 00274 00275 if (mFolders.count() != 0) { 00276 --mRemainingFolders; 00277 KMFolder *folder = *(mFolders.begin()); 00278 if (folder) { 00279 if (folder->isSystemFolder()) 00280 mLastFolder = i18n(folder->name().utf8()); 00281 else 00282 mLastFolder = folder->name(); 00283 } 00284 mFolders.erase(mFolders.begin()); 00285 if (folder) { 00286 folder->open(); 00287 mOpenedFolders.append(folder); 00288 for(int i = 0; i < folder->count(); ++i) { 00289 Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(folder, i); 00290 ++mRemainingMessages; 00291 // Prepend message to the list to search newest mail first. 00292 mSerNums.prepend(serNum); 00293 } 00294 } 00295 mProcessNextBatchTimer->start(0, true); 00296 return; 00297 } 00298 if (mRemainingFolders == 0) { 00299 mRunning = false; 00300 QValueListConstIterator<QGuardedPtr<KMFolder> > it; 00301 for (it = mOpenedFolders.begin(); it != mOpenedFolders.end(); ++it) { 00302 KMFolder *folder = *it; 00303 if (folder) 00304 folder->close(); 00305 } 00306 mOpenedFolders.clear(); 00307 mRemainingMessages = -1; 00308 mRemainingFolders = -1; 00309 mFolders.clear(); 00310 mLastFolder = ""; 00311 emit finished(true); 00312 return; 00313 } 00314 00315 //wait for imap folders to be retrieved 00316 mIdle = true; 00317 } 00318 00319 void KMSearch::slotFolderComplete(KMFolderImap *folder, bool success) 00320 { 00321 disconnect(folder, SIGNAL(folderComplete(KMFolderImap*, bool)), 00322 this, SLOT(slotFolderComplete(KMFolderImap*, bool))); 00323 00324 if (success) { 00325 //if !(mSearchPattern.hasBodyRules()) 00326 mFolders.append(folder->folder()); 00327 //else 00328 // get all the bodies of messages in the folder, need kroupware body caching 00329 if (mIdle) 00330 mProcessNextBatchTimer->start(0, true); 00331 } else { 00332 stop(); 00333 } 00334 } 00335 00336 00337 //----------------------------------------------------------------------------- 00338 KMFolderSearch::KMFolderSearch(KMFolder* folder, const char* name) 00339 : FolderStorage(folder, name) 00340 { 00341 mIdsStream = 0; 00342 mSearch = 0; 00343 mInvalid = false; 00344 mUnlinked = true; 00345 mTempOpened = false; 00346 setNoChildren(true); 00347 00348 //Hook up some slots for live updating of search folders 00349 //TODO: Optimize folderInvalidated, folderAdded, folderRemoved 00350 connect(kmkernel->folderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)), 00351 this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32))); 00352 connect(kmkernel->folderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)), 00353 this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32))); 00354 connect(kmkernel->folderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)), 00355 this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int))); 00356 connect(kmkernel->folderMgr(), SIGNAL(folderInvalidated(KMFolder*)), 00357 this, SLOT(examineInvalidatedFolder(KMFolder*))); 00358 connect(kmkernel->folderMgr(), SIGNAL(folderAdded(KMFolder*)), 00359 this, SLOT(examineInvalidatedFolder(KMFolder*))); 00360 connect(kmkernel->folderMgr(), SIGNAL(folderRemoved(KMFolder*)), 00361 this, SLOT(examineRemovedFolder(KMFolder*))); 00362 connect(kmkernel->folderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)), 00363 this, SLOT(propagateHeaderChanged(KMFolder*,int))); 00364 00365 connect(kmkernel->imapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)), 00366 this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32))); 00367 connect(kmkernel->imapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)), 00368 this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32))); 00369 connect(kmkernel->imapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)), 00370 this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int))); 00371 connect(kmkernel->imapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)), 00372 this, SLOT(examineInvalidatedFolder(KMFolder*))); 00373 connect(kmkernel->imapFolderMgr(), SIGNAL(folderAdded(KMFolder*)), 00374 this, SLOT(examineInvalidatedFolder(KMFolder*))); 00375 connect(kmkernel->imapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)), 00376 this, SLOT(examineRemovedFolder(KMFolder*))); 00377 connect(kmkernel->imapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)), 00378 this, SLOT(propagateHeaderChanged(KMFolder*,int))); 00379 00380 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)), 00381 this, SLOT(examineAddedMessage(KMFolder*, Q_UINT32))); 00382 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)), 00383 this, SLOT(examineRemovedMessage(KMFolder*, Q_UINT32))); 00384 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgChanged(KMFolder*, Q_UINT32, int)), 00385 this, SLOT(examineChangedMessage(KMFolder*, Q_UINT32, int))); 00386 connect(kmkernel->dimapFolderMgr(), SIGNAL(folderInvalidated(KMFolder*)), 00387 this, SLOT(examineInvalidatedFolder(KMFolder*))); 00388 connect(kmkernel->dimapFolderMgr(), SIGNAL(folderAdded(KMFolder*)), 00389 this, SLOT(examineInvalidatedFolder(KMFolder*))); 00390 connect(kmkernel->dimapFolderMgr(), SIGNAL(folderRemoved(KMFolder*)), 00391 this, SLOT(examineRemovedFolder(KMFolder*))); 00392 connect(kmkernel->dimapFolderMgr(), SIGNAL(msgHeaderChanged(KMFolder*,int)), 00393 this, SLOT(propagateHeaderChanged(KMFolder*,int))); 00394 00395 mExecuteSearchTimer = new QTimer(); 00396 connect(mExecuteSearchTimer, SIGNAL(timeout()), 00397 this, SLOT(executeSearch())); 00398 } 00399 00400 KMFolderSearch::~KMFolderSearch() 00401 { 00402 delete mExecuteSearchTimer; 00403 delete mSearch; 00404 mSearch = 0; 00405 if (mOpenCount > 0) 00406 close(TRUE); 00407 } 00408 00409 void KMFolderSearch::setSearch(KMSearch *search) 00410 { 00411 truncateIndex(); //new search old index is obsolete 00412 emit cleared(); 00413 mInvalid = false; 00414 setDirty( true ); //have to write the index 00415 if (!mUnlinked) { 00416 unlink(QFile::encodeName(indexLocation())); 00417 mUnlinked = true; 00418 } 00419 if (mSearch != search) { 00420 delete mSearch; 00421 mSearch = search; // take ownership 00422 if (mSearch) { 00423 QObject::connect(search, SIGNAL(found(Q_UINT32)), 00424 SLOT(addSerNum(Q_UINT32))); 00425 QObject::connect(search, SIGNAL(finished(bool)), 00426 SLOT(searchFinished(bool))); 00427 } 00428 } 00429 if (mSearch) 00430 mSearch->write(location()); 00431 clearIndex(); 00432 mTotalMsgs = 0; 00433 mUnreadMsgs = 0; 00434 emit numUnreadMsgsChanged( folder() ); 00435 emit changed(); // really want a kmfolder cleared signal 00436 /* TODO There is KMFolder::cleared signal now. Adjust. */ 00437 if (mSearch) 00438 mSearch->start(); 00439 open(); // will be closed in searchFinished 00440 } 00441 00442 void KMFolderSearch::executeSearch() 00443 { 00444 if (mSearch) 00445 mSearch->stop(); 00446 setSearch(mSearch); 00447 if ( folder()->parent() ) 00448 folder()->parent()->manager()->invalidateFolder(kmkernel->msgDict(), folder() ); 00449 } 00450 00451 const KMSearch* KMFolderSearch::search() const 00452 { 00453 return mSearch; 00454 } 00455 00456 void KMFolderSearch::searchFinished(bool success) 00457 { 00458 if (!success) 00459 mSerNums.clear(); 00460 close(); 00461 } 00462 00463 void KMFolderSearch::addSerNum(Q_UINT32 serNum) 00464 { 00465 if (mInvalid) // A new search is scheduled don't bother doing anything 00466 return; 00467 int idx = -1; 00468 KMFolder *aFolder = 0; 00469 kmkernel->msgDict()->getLocation(serNum, &aFolder, &idx); 00470 assert(aFolder && (idx != -1)); 00471 if(mFolders.findIndex(aFolder) == -1) { 00472 aFolder->open(); 00473 // Exceptional case, for when folder has invalid ids 00474 if (mInvalid) 00475 return; 00476 mFolders.append(aFolder); 00477 } 00478 setDirty( true ); //TODO append a single entry to .ids file and sync. 00479 if (!mUnlinked) { 00480 unlink(QFile::encodeName(indexLocation())); 00481 mUnlinked = true; 00482 } 00483 mSerNums.append(serNum); 00484 KMMsgBase *mb = aFolder->getMsgBase(idx); 00485 if (mb->isUnread() || mb->isNew()) { 00486 if (mUnreadMsgs == -1) 00487 mUnreadMsgs = 0; 00488 ++mUnreadMsgs; 00489 emit numUnreadMsgsChanged( folder() ); 00490 } 00491 emitMsgAddedSignals(mSerNums.count()-1); 00492 } 00493 00494 void KMFolderSearch::removeSerNum(Q_UINT32 serNum) 00495 { 00496 QValueVector<Q_UINT32>::const_iterator it; 00497 int i = 0; 00498 for(it = mSerNums.begin(); it != mSerNums.end(); ++it, ++i) 00499 if ((*it) == serNum) { 00500 int idx = -1; 00501 KMFolder *aFolder = 0; 00502 kmkernel->msgDict()->getLocation(serNum, &aFolder, &idx); 00503 assert(aFolder && (idx != -1)); 00504 emit msgRemoved(folder(), serNum); 00505 removeMsg(i); 00506 return; 00507 } 00508 if (!mUnlinked) { 00509 unlink(QFile::encodeName(indexLocation())); 00510 mUnlinked = true; 00511 } 00512 } 00513 00514 QCString& KMFolderSearch::getMsgString(int idx, QCString& mDest) 00515 { 00516 KMFolder *folder = getMsgBase(idx)->parent(); 00517 assert(folder); 00518 return folder->getMsgString(folder->find(getMsgBase(idx)), mDest); 00519 } 00520 00521 int KMFolderSearch::addMsg(KMMessage*, int* index_return) 00522 { 00523 //Not supported search folder can't own messages 00524 *index_return = -1; 00525 return 0; 00526 } 00527 00528 bool KMFolderSearch::readSearch() 00529 { 00530 mSearch = new KMSearch; 00531 QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32))); 00532 QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool))); 00533 return mSearch->read(location()); 00534 } 00535 00536 int KMFolderSearch::open() 00537 { 00538 mOpenCount++; 00539 kmkernel->jobScheduler()->notifyOpeningFolder( folder() ); 00540 if (mOpenCount > 1) 00541 return 0; // already open 00542 00543 readConfig(); 00544 if (!mSearch && !readSearch()) 00545 return -1; 00546 00547 emit cleared(); 00548 if (!mSearch || !search()->running()) 00549 if (!readIndex()) { 00550 executeSearch(); 00551 } 00552 00553 return 0; 00554 } 00555 00556 int KMFolderSearch::canAccess() 00557 { 00558 assert(!folder()->name().isEmpty()); 00559 00560 if (access(QFile::encodeName(location()), R_OK | W_OK | X_OK) != 0) 00561 return 1; 00562 return 0; 00563 } 00564 00565 void KMFolderSearch::sync() 00566 { 00567 if (mDirty) { 00568 if (mSearch) 00569 mSearch->write(location()); 00570 updateIndex(); 00571 } 00572 } 00573 00574 void KMFolderSearch::close(bool force) 00575 { 00576 if (mOpenCount <= 0) return; 00577 if (mOpenCount > 0) mOpenCount--; 00578 if (mOpenCount > 0 && !force) return; 00579 00580 if (mAutoCreateIndex) { 00581 if (mSearch) 00582 mSearch->write(location()); 00583 updateIndex(); 00584 if (mSearch && search()->running()) 00585 mSearch->stop(); 00586 writeConfig(); 00587 } 00588 00589 //close all referenced folders 00590 QValueListIterator<QGuardedPtr<KMFolder> > fit; 00591 for (fit = mFolders.begin(); fit != mFolders.end(); ++fit) { 00592 if (!(*fit)) 00593 continue; 00594 (*fit)->close(); 00595 } 00596 mFolders.clear(); 00597 00598 clearIndex(TRUE); 00599 00600 if (mIdsStream) 00601 fclose(mIdsStream); 00602 00603 mOpenCount = 0; 00604 mIdsStream = 0; 00605 mUnreadMsgs = -1; 00606 } 00607 00608 int KMFolderSearch::create(bool) 00609 { 00610 int old_umask; 00611 int rc = unlink(QFile::encodeName(location())); 00612 if (!rc) 00613 return rc; 00614 rc = 0; 00615 00616 assert(!folder()->name().isEmpty()); 00617 assert(mOpenCount == 0); 00618 00619 kdDebug(5006) << "Creating folder " << location() << endl; 00620 if (access(QFile::encodeName(location()), F_OK) == 0) { 00621 kdDebug(5006) << "KMFolderSearch::create call to access function failed." 00622 << endl; 00623 return EEXIST; 00624 } 00625 00626 old_umask = umask(077); 00627 FILE *mStream = fopen(QFile::encodeName(location()), "w+"); 00628 umask(old_umask); 00629 if (!mStream) return errno; 00630 fclose(mStream); 00631 00632 clearIndex(); 00633 if (!mSearch) { 00634 mSearch = new KMSearch(); 00635 QObject::connect(mSearch, SIGNAL(found(Q_UINT32)), SLOT(addSerNum(Q_UINT32))); 00636 QObject::connect(mSearch, SIGNAL(finished(bool)), SLOT(searchFinished(bool))); 00637 } 00638 mSearch->write(location()); 00639 mOpenCount++; 00640 mChanged = false; 00641 mUnreadMsgs = 0; 00642 mTotalMsgs = 0; 00643 return rc; 00644 } 00645 00646 int KMFolderSearch::compact( bool ) 00647 { 00648 needsCompact = false; 00649 return 0; 00650 } 00651 00652 bool KMFolderSearch::isReadOnly() const 00653 { 00654 return false; //TODO: Make it true and get that working ok 00655 } 00656 00657 FolderJob* KMFolderSearch::doCreateJob(KMMessage*, FolderJob::JobType, 00658 KMFolder*, QString, const AttachmentStrategy* ) const 00659 { 00660 // Should never be called 00661 assert(0); 00662 return 0; 00663 } 00664 00665 FolderJob* KMFolderSearch::doCreateJob(QPtrList<KMMessage>&, const QString&, 00666 FolderJob::JobType, KMFolder*) const 00667 { 00668 // Should never be called 00669 assert(0); 00670 return 0; 00671 } 00672 00673 const KMMsgBase* KMFolderSearch::getMsgBase(int idx) const 00674 { 00675 int folderIdx = -1; 00676 KMFolder *folder = 0; 00677 if (idx < 0 || (Q_UINT32)idx >= mSerNums.count()) 00678 return 0; 00679 kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx); 00680 assert(folder && (folderIdx != -1)); 00681 return folder->getMsgBase(folderIdx); 00682 } 00683 00684 KMMsgBase* KMFolderSearch::getMsgBase(int idx) 00685 { 00686 int folderIdx = -1; 00687 KMFolder *folder = 0; 00688 if (idx < 0 || (Q_UINT32)idx >= mSerNums.count()) 00689 return 0; 00690 kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx); 00691 if (!folder || folderIdx == -1) 00692 return 0; //exceptional case 00693 return folder->getMsgBase(folderIdx); 00694 } 00695 00696 //----------------------------------------------------------------------------- 00697 KMMessage* KMFolderSearch::getMsg(int idx) 00698 { 00699 int folderIdx = -1; 00700 KMFolder *folder = 0; 00701 if (idx < 0 || (Q_UINT32)idx >= mSerNums.count()) 00702 return 0; 00703 kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx); 00704 assert(folder && (folderIdx != -1)); 00705 KMMessage* msg = folder->getMsg( folderIdx ); 00706 return msg; 00707 } 00708 00709 //------------------------------------------------------------- 00710 void 00711 KMFolderSearch::ignoreJobsForMessage( KMMessage* msg ) 00712 { 00713 if ( !msg || msg->transferInProgress() ) 00714 return; 00715 /* While non-imap folders manage their jobs themselves, imap ones let 00716 their account manage them. Therefor first clear the jobs managed by 00717 this folder via the inherited method, then clear the imap ones. */ 00718 FolderStorage::ignoreJobsForMessage( msg ); 00719 00720 if (msg->parent()->folderType() == KMFolderTypeImap) { 00721 KMAcctImap *account = 00722 static_cast<KMFolderImap*>( msg->storage() )->account(); 00723 if( !account ) 00724 return; 00725 account->ignoreJobsForMessage( msg ); 00726 } 00727 } 00728 00729 00730 int KMFolderSearch::find(const KMMsgBase* msg) const 00731 { 00732 int pos = 0; 00733 Q_UINT32 serNum = msg->getMsgSerNum(); 00734 QValueVector<Q_UINT32>::const_iterator it; 00735 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) { 00736 if ((*it) == serNum) 00737 return pos; 00738 ++pos; 00739 } 00740 return -1; 00741 } 00742 00743 QString KMFolderSearch::indexLocation() const 00744 { 00745 QString sLocation(folder()->path()); 00746 00747 if (!sLocation.isEmpty()) sLocation += '/'; 00748 sLocation += '.'; 00749 sLocation += dotEscape(fileName()); 00750 sLocation += ".index"; 00751 sLocation += ".search"; 00752 00753 return sLocation; 00754 } 00755 00756 int KMFolderSearch::updateIndex() 00757 { 00758 if (mSearch && search()->running()) 00759 unlink(QFile::encodeName(indexLocation())); 00760 else 00761 if (dirty()) 00762 return writeIndex(); 00763 return 0; 00764 } 00765 00766 int KMFolderSearch::writeIndex( bool ) 00767 { 00768 // TODO:If we fail to write the index we should panic the kernel 00769 // TODO:and the same for other folder types too, and the msgDict. 00770 QString filename = indexLocation(); 00771 int old_umask = umask(077); 00772 QString tempName = filename + ".temp"; 00773 unlink(QFile::encodeName(tempName)); 00774 00775 // We touch the folder, otherwise the index is regenerated, if KMail is 00776 // running, while the clock switches from daylight savings time to normal time 00777 utime(QFile::encodeName(location()), 0); 00778 00779 FILE *tmpIndexStream = fopen(QFile::encodeName(tempName), "w"); 00780 umask(old_umask); 00781 00782 if (!tmpIndexStream) { 00783 kdDebug(5006) << "Cannot write '" << filename 00784 << strerror(errno) << " (" << errno << ")" << endl; 00785 truncate(QFile::encodeName(filename), 0); 00786 return -1; 00787 } 00788 fprintf(tmpIndexStream, IDS_HEADER, IDS_VERSION); 00789 Q_UINT32 byteOrder = 0x12345678; 00790 fwrite(&byteOrder, sizeof(byteOrder), 1, tmpIndexStream); 00791 00792 Q_UINT32 count = mSerNums.count(); 00793 if (!fwrite(&count, sizeof(count), 1, tmpIndexStream)) { 00794 fclose(tmpIndexStream); 00795 truncate(QFile::encodeName(filename), 0); 00796 return -1; 00797 } 00798 00799 QValueVector<Q_UINT32>::iterator it; 00800 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) { 00801 Q_UINT32 serNum = *it; 00802 if (!fwrite(&serNum, sizeof(serNum), 1, tmpIndexStream)) 00803 return -1; 00804 } 00805 if (ferror(tmpIndexStream)) return ferror(tmpIndexStream); 00806 if (fflush(tmpIndexStream) != 0) return errno; 00807 if (fsync(fileno(tmpIndexStream)) != 0) return errno; 00808 if (fclose(tmpIndexStream) != 0) return errno; 00809 00810 ::rename(QFile::encodeName(tempName), QFile::encodeName(indexLocation())); 00811 mDirty = FALSE; 00812 mUnlinked = FALSE; 00813 00814 return 0; 00815 } 00816 00817 DwString KMFolderSearch::getDwString(int idx) 00818 { 00819 return getMsgBase(idx)->parent()->getDwString( idx ); 00820 } 00821 00822 KMMessage* KMFolderSearch::readMsg(int idx) 00823 { 00824 int folderIdx = -1; 00825 KMFolder *folder = 0; 00826 kmkernel->msgDict()->getLocation(mSerNums[idx], &folder, &folderIdx); 00827 assert(folder && (folderIdx != -1)); 00828 return folder->getMsg( folderIdx ); 00829 } 00830 00831 bool KMFolderSearch::readIndex() 00832 { 00833 clearIndex(); 00834 QString filename = indexLocation(); 00835 mIdsStream = fopen(QFile::encodeName(filename), "r+"); 00836 if (!mIdsStream) 00837 return false; 00838 00839 int version = 0; 00840 fscanf(mIdsStream, IDS_HEADER, &version); 00841 if (version != IDS_VERSION) { 00842 fclose(mIdsStream); 00843 mIdsStream = 0; 00844 return false; 00845 } 00846 bool swapByteOrder; 00847 Q_UINT32 byte_order; 00848 if (!fread(&byte_order, sizeof(byte_order), 1, mIdsStream)) { 00849 fclose(mIdsStream); 00850 mIdsStream = 0; 00851 return false; 00852 } 00853 swapByteOrder = (byte_order == 0x78563412); 00854 00855 Q_UINT32 count; 00856 if (!fread(&count, sizeof(count), 1, mIdsStream)) { 00857 fclose(mIdsStream); 00858 mIdsStream = 0; 00859 return false; 00860 } 00861 if (swapByteOrder) 00862 count = kmail_swap_32(count); 00863 00864 mUnreadMsgs = 0; 00865 mSerNums.reserve(count); 00866 for (unsigned int index = 0; index < count; index++) { 00867 Q_UINT32 serNum; 00868 int folderIdx = -1; 00869 KMFolder *folder = 0; 00870 bool readOk = fread(&serNum, sizeof(serNum), 1, mIdsStream); 00871 if (!readOk) { 00872 clearIndex(); 00873 fclose(mIdsStream); 00874 mIdsStream = 0; 00875 return false; 00876 } 00877 if (swapByteOrder) 00878 serNum = kmail_swap_32(serNum); 00879 00880 kmkernel->msgDict()->getLocation( serNum, &folder, &folderIdx ); 00881 if (!folder || (folderIdx == -1)) { 00882 clearIndex(); 00883 fclose(mIdsStream); 00884 mIdsStream = 0; 00885 return false; 00886 } 00887 mSerNums.push_back(serNum); 00888 if(mFolders.findIndex(folder) == -1) { 00889 folder->open(); 00890 if (mInvalid) //exceptional case for when folder has invalid ids 00891 return false; 00892 mFolders.append(folder); 00893 } 00894 KMMsgBase *mb = folder->getMsgBase(folderIdx); 00895 if (!mb) //Exceptional case our .ids file is messed up 00896 return false; 00897 if (mb->isNew() || mb->isUnread()) { 00898 if (mUnreadMsgs == -1) ++mUnreadMsgs; 00899 ++mUnreadMsgs; 00900 } 00901 } 00902 mTotalMsgs = mSerNums.count(); 00903 fclose(mIdsStream); 00904 mIdsStream = 0; 00905 mUnlinked = true; 00906 return true; 00907 } 00908 00909 int KMFolderSearch::removeContents() 00910 { 00911 unlink(QFile::encodeName(location())); 00912 unlink(QFile::encodeName(indexLocation())); 00913 mUnlinked = true; 00914 return 0; 00915 } 00916 00917 int KMFolderSearch::expungeContents() 00918 { 00919 setSearch(new KMSearch()); 00920 return 0; 00921 } 00922 00923 int KMFolderSearch::count(bool cache) const 00924 { 00925 Q_UNUSED(cache); 00926 return mSerNums.count(); 00927 } 00928 00929 KMMsgBase* KMFolderSearch::takeIndexEntry(int idx) 00930 { 00931 assert(idx >= 0 && idx < (int)mSerNums.count()); 00932 KMMsgBase *msgBase = getMsgBase(idx); 00933 QValueVector<Q_UINT32>::iterator it = mSerNums.begin(); 00934 mSerNums.erase(&it[idx]); 00935 return msgBase; 00936 } 00937 00938 KMMsgInfo* KMFolderSearch::setIndexEntry(int idx, KMMessage *msg) 00939 { 00940 assert(idx >= 0 && idx < (int)mSerNums.count()); 00941 Q_UNUSED( idx ); 00942 return msg->storage()->setIndexEntry(msg->parent()->find(msg), msg); 00943 } 00944 00945 void KMFolderSearch::clearIndex(bool, bool) 00946 { 00947 mSerNums.clear(); 00948 } 00949 00950 void KMFolderSearch::fillDictFromIndex(KMMsgDict *) 00951 { 00952 // No dict for search folders, as search folders don't own any messages 00953 return; 00954 } 00955 00956 void KMFolderSearch::truncateIndex() 00957 { 00958 truncate(QFile::encodeName(indexLocation()), IDS_HEADER_LEN); 00959 } 00960 00961 void KMFolderSearch::examineAddedMessage(KMFolder *aFolder, Q_UINT32 serNum) 00962 { 00963 if (!search() && !readSearch()) 00964 return; 00965 if (!search()->inScope(aFolder)) 00966 return; 00967 if (!mTempOpened) { 00968 open(); 00969 mTempOpened = true; 00970 } 00971 00972 if (!search()->searchPattern()) 00973 return; 00974 00975 int idx = -1; 00976 KMFolder *folder = 0; 00977 kmkernel->msgDict()->getLocation(serNum, &folder, &idx); 00978 assert(folder && (idx != -1)); 00979 assert(folder == aFolder); 00980 if (!folder->isOpened()) 00981 return; 00982 00983 if (folder->folderType() == KMFolderTypeImap) { 00984 // Unless there is a search currently running, add the message to 00985 // the list of ones to check on folderCompleted and hook up the signal. 00986 KMFolderImap *imapFolder = 00987 dynamic_cast<KMFolderImap*> ( folder->storage() ); 00988 if (!mSearch->running()) { 00989 mUnexaminedMessages.push(serNum); 00990 disconnect(imapFolder, SIGNAL(folderComplete(KMFolderImap*, bool)), 00991 this, SLOT (examineCompletedFolder(KMFolderImap*, bool))); 00992 connect(imapFolder, SIGNAL(folderComplete(KMFolderImap*, bool)), 00993 this, SLOT (examineCompletedFolder(KMFolderImap*, bool))); 00994 } 00995 } else { 00996 if (search()->searchPattern()->matches(serNum)) 00997 if (mSearch->running()) { 00998 mSearch->stop(); 00999 mExecuteSearchTimer->start(0, true); 01000 } else { 01001 addSerNum(serNum); 01002 } 01003 } 01004 } 01005 01006 void KMFolderSearch::examineCompletedFolder(KMFolderImap *aFolder, bool success) 01007 { 01008 disconnect (aFolder, SIGNAL(folderComplete(KMFolderImap*, bool)), 01009 this, SLOT(examineCompletedFolder(KMFolderImap*, bool))); 01010 if (!success) return; 01011 Q_UINT32 serNum; 01012 while (!mUnexaminedMessages.isEmpty()) { 01013 serNum = mUnexaminedMessages.pop(); 01014 if (search()->searchPattern()->matches(serNum)) 01015 addSerNum(serNum); 01016 } 01017 } 01018 01019 void KMFolderSearch::examineRemovedMessage(KMFolder *folder, Q_UINT32 serNum) 01020 { 01021 if (!search() && !readSearch()) 01022 return; 01023 if (!search()->inScope(folder)) 01024 return; 01025 if (!mTempOpened) { 01026 open(); 01027 mTempOpened = true; 01028 } 01029 01030 if (mSearch->running()) { 01031 mSearch->stop(); 01032 mExecuteSearchTimer->start(0, true); 01033 } else { 01034 removeSerNum(serNum); 01035 } 01036 } 01037 01038 void KMFolderSearch::examineChangedMessage(KMFolder *aFolder, Q_UINT32 serNum, int delta) 01039 { 01040 if (!search() && !readSearch()) 01041 return; 01042 if (!search()->inScope(aFolder)) 01043 return; 01044 if (!mTempOpened) { 01045 open(); 01046 mTempOpened = true; 01047 } 01048 QValueVector<Q_UINT32>::const_iterator it; 01049 it = qFind( mSerNums.begin(), mSerNums.end(), serNum ); 01050 if (it != mSerNums.end()) { 01051 mUnreadMsgs += delta; 01052 emit numUnreadMsgsChanged( folder() ); 01053 emit msgChanged( folder(), serNum, delta ); 01054 } 01055 } 01056 01057 void KMFolderSearch::examineInvalidatedFolder(KMFolder *folder) 01058 { 01059 if (!search() && !readSearch()) 01060 return; 01061 if (!search()->inScope(folder)) 01062 return; 01063 if (mTempOpened) { 01064 close(); 01065 mTempOpened = false; 01066 } 01067 01068 mInvalid = true; 01069 if (mSearch) 01070 mSearch->stop(); 01071 01072 if (!mUnlinked) { 01073 unlink(QFile::encodeName(indexLocation())); 01074 mUnlinked = true; 01075 } 01076 01077 if (!isOpened()) //give up, until the user manually opens the folder 01078 return; 01079 01080 if (!mTempOpened) { 01081 open(); 01082 mTempOpened = true; 01083 } 01084 mExecuteSearchTimer->start(0, true); 01085 } 01086 01087 void KMFolderSearch::examineRemovedFolder(KMFolder *folder) 01088 { 01089 examineInvalidatedFolder(folder); 01090 if (mSearch->root() == folder) { 01091 delete mSearch; 01092 mSearch = 0; 01093 } 01094 } 01095 01096 void KMFolderSearch::propagateHeaderChanged(KMFolder *aFolder, int idx) 01097 { 01098 int pos = 0; 01099 if (!search() && !readSearch()) 01100 return; 01101 if (!search()->inScope(aFolder)) 01102 return; 01103 if (!mTempOpened) { 01104 open(); 01105 mTempOpened = true; 01106 } 01107 01108 Q_UINT32 serNum = kmkernel->msgDict()->getMsgSerNum(aFolder, idx); 01109 QValueVector<Q_UINT32>::const_iterator it; 01110 for(it = mSerNums.begin(); it != mSerNums.end(); ++it) { 01111 if ((*it) == serNum) { 01112 emit msgHeaderChanged(folder(), pos); 01113 return; 01114 } 01115 ++pos; 01116 } 01117 } 01118 01119 void KMFolderSearch::tryReleasingFolder(KMFolder* folder) 01120 { 01121 // We'll succeed releasing the folder only if mTempOpened and mOpenCount==1. 01122 // Otherwise if mOpenCount>1 (e.g while the search dialog is up), we would just keep closing/reopening for nothing 01123 if ( mTempOpened && mOpenCount == 1 ) 01124 { 01125 examineInvalidatedFolder( folder ); 01126 } 01127 } 01128 01129 #include "kmfoldersearch.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:48 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003