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