kmail Library API Documentation

kmmsgindex.cpp

00001 // Author: Sam Magnuson <zachsman@wiw.org> 00002 // License GPL -- indexing logic for KMail 00003 00004 #ifdef HAVE_CONFIG_H 00005 #include <config.h> 00006 #endif 00007 00008 #include "kmmsgindex.h" 00009 00010 #include "kmsearchpattern.h" 00011 #include "kmfoldersearch.h" 00012 #include "kmfoldermgr.h" 00013 #include "kmmsgdict.h" 00014 #include "kmkernel.h" 00015 00016 #include "mimelib/message.h" 00017 #include "mimelib/headers.h" 00018 #include "mimelib/utility.h" 00019 #include "mimelib/enum.h" 00020 #include "mimelib/body.h" 00021 #include "mimelib/bodypart.h" 00022 #include "mimelib/field.h" 00023 00024 #include <kdebug.h> 00025 00026 #include <qdict.h> 00027 #include <qapplication.h> 00028 00029 #include <stdio.h> 00030 #include <unistd.h> 00031 #include <fcntl.h> 00032 #include <sys/mman.h> 00033 #include <sys/types.h> 00034 #include <sys/stat.h> 00035 #include <ctype.h> 00036 #include <errno.h> 00037 00038 //#define USE_MMAP 00039 class 00040 KMMsgIndexRef 00041 { 00042 #ifdef USE_MMAP 00043 Q_UINT32 *mRef; 00044 #endif 00045 int mFD, mSize; 00046 public: 00047 KMMsgIndexRef(int f, int size); 00048 ~KMMsgIndexRef() { } 00049 bool error(); 00050 00051 void resize(int size); 00052 void sync(); 00053 00054 bool write(int off, Q_UINT32 val); 00055 Q_UINT32 read(int off, bool *ok=NULL); 00056 }; 00057 KMMsgIndexRef::KMMsgIndexRef(int f, int size) : 00058 mFD(f), mSize(size) 00059 { 00060 #ifdef USE_MMAP 00061 if(mSize != 0) 00062 mRef = (Q_UINT32*)mmap(0, mSize * sizeof(Q_INT32), 00063 PROT_READ|PROT_WRITE, MAP_SHARED, mFD, 0); 00064 else 00065 mRef = (Q_UINT32*)MAP_FAILED; 00066 #endif 00067 } 00068 void KMMsgIndexRef::sync() 00069 { 00070 #ifdef USE_MMAP 00071 if(mRef != MAP_FAILED) 00072 msync(mRef, mSize * sizeof(Q_INT32), MS_SYNC); 00073 #endif 00074 } 00075 bool KMMsgIndexRef::error() 00076 { 00077 #ifdef USE_MMAP 00078 if(mRef == MAP_FAILED) 00079 return TRUE; 00080 #endif 00081 return FALSE; 00082 } 00083 void KMMsgIndexRef::resize(int newSize) 00084 { 00085 #ifdef USE_MMAP 00086 if(mRef != MAP_FAILED) 00087 munmap(mRef, mSize * sizeof(Q_INT32)); 00088 if(ftruncate(mFD, newSize * sizeof(Q_INT32)) == -1) { 00089 for(Q_INT32 i = mSize; i < newSize; i++) 00090 ::write(mFD, &i, sizeof(i)); 00091 } 00092 #endif 00093 mSize = newSize; 00094 #ifdef USE_MMAP 00095 mRef = (Q_UINT32*)mmap(0, mSize * sizeof(Q_INT32), 00096 PROT_READ|PROT_WRITE, MAP_SHARED, mFD, 0); 00097 #endif 00098 } 00099 bool KMMsgIndexRef::write(int off, Q_UINT32 val) 00100 { 00101 if(off > mSize) 00102 return FALSE; 00103 #ifdef USE_MMAP 00104 mRef[off] = val; 00105 #else 00106 lseek(mFD, off * sizeof(Q_INT32), SEEK_SET); 00107 ::write(mFD, &val, sizeof(val)); 00108 #endif 00109 return TRUE; 00110 } 00111 Q_UINT32 KMMsgIndexRef::read(int off, bool *ok) 00112 { 00113 if(off > mSize) { 00114 if(ok) 00115 *ok = FALSE; 00116 return 0; 00117 } 00118 #ifdef USE_MMAP 00119 return mRef[off]; 00120 #else 00121 Q_UINT32 ret; 00122 lseek(mFD, off * sizeof(Q_INT32), SEEK_SET); 00123 ::read(mFD, &ret, sizeof(ret)); 00124 return ret; 00125 #endif 00126 return 0; 00127 } 00128 00129 inline bool 00130 km_isSeparator(const char *content, uint i, uint content_len) 00131 { 00132 return !(isalnum(content[i]) || 00133 (i < content_len - 1 && content[i+1] != '\n' && 00134 content[i+1] != '\t' && content[i+1] != ' ' && 00135 (content[i] == '.' || content[i] == '-' || 00136 content[i] == '\\' || content[i] == '/' || 00137 content[i] == '\'' || content[i] == ':'))); 00138 } 00139 00140 inline bool 00141 km_isSeparator(const QChar *content, uint i, uint content_len) 00142 { 00143 return !(content[i].isLetterOrNumber() || 00144 (i < content_len - 1 && content[i+1] != '\n' && 00145 content[i+1] != '\t' && content[i+1] != ' ' && 00146 (content[i] == '.' || content[i] == '-' || 00147 content[i] == '\\' || content[i] == '/' || 00148 content[i] == '\'' || content[i] == ':'))); 00149 } 00150 00151 inline bool 00152 km_isSeparator(const QString &s, int i, int content_len=-1) 00153 { 00154 return km_isSeparator(s.unicode(), i, 00155 content_len < 0 ? s.length() : content_len); 00156 } 00157 00158 inline bool 00159 km_isSeparated(const QString &f) 00160 { 00161 for(uint i=0, l=f.length(); i < f.length(); i++) { 00162 if(km_isSeparator(f.unicode(), i, l)) 00163 return TRUE; 00164 } 00165 return FALSE; 00166 } 00167 00168 inline QStringList 00169 km_separate(const QString &f) 00170 { 00171 if(!km_isSeparated(f)) 00172 return QStringList(f); 00173 QStringList ret; 00174 uint i_o = 0; 00175 for(uint i=0, l=f.length(); i < f.length(); i++) { 00176 if(km_isSeparator(f.unicode(), i, l)) { 00177 QString chnk = f.mid(i_o, i - i_o).latin1(); 00178 if(!chnk.isEmpty()) 00179 ret << chnk; 00180 i_o = i+1; 00181 } 00182 } 00183 if(i_o != f.length()) { 00184 QString chnk = f.mid(i_o, f.length() - i_o); 00185 if(!chnk.isEmpty()) 00186 ret << chnk; 00187 } 00188 return ret; 00189 } 00190 00191 enum { 00192 HEADER_BYTEORDER = 0, 00193 HEADER_VERSION = 1, 00194 HEADER_COMPLETE = 2, 00195 HEADER_COUNT = 3, 00196 HEADER_USED = 4, 00197 HEADER_INDEXED = 5, 00198 HEADER_REMOVED = 6, 00199 HEADER_end = 7, 00200 00201 CHUNK_HEADER_COUNT = 0, 00202 CHUNK_HEADER_USED = 1, 00203 CHUNK_HEADER_NEXT = 2, 00204 CHUNK_HEADER_end = 3, 00205 00206 TOC_BODY = 0, 00207 TOC_HEADER_NAME = 1, 00208 TOC_HEADER_DATA = 2 00209 }; 00210 #define KMMSGINDEX_VERSION 6067 00211 static int kmindex_grow_increment = 40960; //grow this many buckets at a time 00212 00213 KMMsgIndex::KMMsgIndex(QObject *o, const char *n) : 00214 QObject(o, n), mIndexState(INDEX_IDLE), delay_cnt(0), mLastSearch() 00215 { 00216 mTermIndex.loc = kmkernel->folderMgr()->basePath() + "/.kmmsgindex_search"; 00217 mTermTOC.loc = kmkernel->folderMgr()->basePath() + "/.kmmsgindex_toc"; 00218 mTermProcessed.loc = kmkernel->folderMgr()->basePath() + 00219 "/.kmmsgindex_processed"; 00220 } 00221 00222 void KMMsgIndex::init() 00223 { 00224 mActiveSearches.setAutoDelete(TRUE); 00225 reset(FALSE); 00226 readIndex(); 00227 connect(kmkernel->folderMgr(), SIGNAL(msgRemoved(KMFolder*, Q_UINT32)), 00228 this, SLOT(slotRemoveMsg(KMFolder*, Q_UINT32))); 00229 connect(kmkernel->folderMgr(), SIGNAL(msgAdded(KMFolder*, Q_UINT32)), 00230 this, SLOT(slotAddMsg(KMFolder*, Q_UINT32))); 00231 } 00232 00233 void KMMsgIndex::remove() 00234 { 00235 unlink(mTermIndex.loc.latin1()); 00236 unlink(mTermTOC.loc.latin1()); 00237 unlink(mTermProcessed.loc.latin1()); 00238 } 00239 00240 // resets the state of the indexer to nothing (if clean it is assumed 00241 // anything not initialized is cleaned up.. 00242 void 00243 KMMsgIndex::reset(bool clean) 00244 { 00245 //active searches 00246 if(clean) 00247 mActiveSearches.clear(); 00248 //create 00249 if(create.timer_id != -1) { 00250 if(clean) 00251 killTimer(create.timer_id); 00252 create.timer_id = -1; 00253 } 00254 //restore 00255 if(restore.timer_id != -1) { 00256 if(clean) 00257 killTimer(restore.timer_id); 00258 restore.timer_id = -1; 00259 } 00260 //TOC 00261 if(clean) 00262 mTermTOC.body.clear(); 00263 if(mTermTOC.fd != -1) { 00264 if(clean) 00265 close(mTermTOC.fd); 00266 mTermTOC.fd = -1; 00267 } 00268 mTermTOC.h.next_hnum = 0; 00269 if(clean) { 00270 mTermTOC.h.header_lookup.clear(); 00271 mTermTOC.h.headers.clear(); 00272 } 00273 //processed 00274 if(mTermProcessed.fd != -1) { 00275 if(clean) 00276 close(mTermProcessed.fd); 00277 mTermProcessed.fd = -1; 00278 } 00279 if(clean) 00280 mTermProcessed.known.clear(); 00281 restore.reading_processed = FALSE; 00282 //index 00283 { 00284 if(clean) 00285 delete mTermIndex.ref; 00286 mTermIndex.ref = NULL; 00287 } 00288 mTermIndex.removed = mTermIndex.indexed = 0; 00289 mTermIndex.used = mTermIndex.count = 0; 00290 if(mTermIndex.fd != -1) { 00291 if(clean) 00292 close(mTermIndex.fd); 00293 mTermIndex.fd = -1; 00294 } 00295 } 00296 00297 // finds cnt buckets and allocates to make room 00298 int 00299 KMMsgIndex::allocTermChunk(int cnt) 00300 { 00301 int ret = mTermIndex.used; 00302 mTermIndex.used += cnt; //update the used 00303 if(mTermIndex.count < mTermIndex.used) { //time for a remap 00304 mTermIndex.count = QMAX(mTermIndex.count + kmindex_grow_increment, 00305 mTermIndex.used); 00306 mTermIndex.ref->resize(mTermIndex.count); 00307 mTermIndex.ref->write(HEADER_COUNT, mTermIndex.count); 00308 } 00309 mTermIndex.ref->write(HEADER_USED, mTermIndex.used); 00310 return ret; 00311 } 00312 00313 // returns whether header is a header we care about.. 00314 bool 00315 KMMsgIndex::isKillHeader(const char *header, uchar header_len) 00316 { 00317 const char *watched_headers[] = { "Subject", "From", "To", "CC", "BCC", 00318 "Reply-To", "Organization", "List-ID", 00319 "X-Mailing-List", "X-Loop", "X-Mailer", 00320 NULL }; 00321 for(int i = 0; watched_headers[i]; i++) { 00322 if(!strncmp(header, watched_headers[i], header_len)) 00323 return FALSE; 00324 } 00325 return TRUE; 00326 } 00327 00328 //returns whether a term is in the stop list.. 00329 bool 00330 KMMsgIndex::isKillTerm(const char *term, uchar term_len) 00331 { 00332 if(!term || term_len < 1) 00333 return TRUE; 00334 if(term_len <= 2) //too few letters.. 00335 return TRUE; 00336 { //no numbers! 00337 int numlen = 0; 00338 if(term[numlen] == '+' || term[numlen] == '-') 00339 numlen++; 00340 for( ; numlen < term_len; numlen++) { 00341 if(!isdigit(term[numlen]) || term[numlen] == '.') 00342 break; 00343 } 00344 if(numlen == term_len - 1 && term[numlen] == '%') 00345 numlen++; 00346 if(numlen == term_len) 00347 return TRUE; 00348 } 00349 { //static kill words list 00350 static QDict<void> *killDict = NULL; 00351 if(!killDict) { 00352 killDict = new QDict<void>(); 00353 const char *kills[] = { "from", "kmail", "is", "in", "and", 00354 "it", "this", "of", "that", "on", 00355 "you", "if", "be", "not", 00356 "with", "for", "to", "the", "but", 00357 NULL }; 00358 for(int i = 0; kills[i]; i++) 00359 killDict->insert(kills[i], (void*)1); 00360 } 00361 if(killDict->find(term)) 00362 return TRUE; 00363 } 00364 return FALSE; 00365 } 00366 00367 // finds a free bucket starting at where to put serNum in the dbase 00368 int 00369 KMMsgIndex::addBucket(int where, Q_UINT32 serNum) 00370 { 00371 int ret = where; 00372 if(where == -1) { 00373 //enough for two (and the tail).. 00374 int first_chunk_size = CHUNK_HEADER_end + 2 + 1; 00375 int off = ret = allocTermChunk(first_chunk_size); 00376 00377 //special case to mark the tail for the first 00378 mTermIndex.ref->write(off, off+1); 00379 off++; 00380 first_chunk_size--; 00381 00382 //now mark in index 00383 mTermIndex.ref->write(off+CHUNK_HEADER_COUNT, first_chunk_size); 00384 mTermIndex.ref->write(off+CHUNK_HEADER_USED, CHUNK_HEADER_end + 1); 00385 mTermIndex.ref->write(off+CHUNK_HEADER_end, serNum); 00386 } else { 00387 uint len = mTermIndex.ref->read(where+CHUNK_HEADER_COUNT); 00388 if(len == mTermIndex.ref->read(where+CHUNK_HEADER_USED)) { 00389 len = 34; //let's make a bit more room this time.. 00390 int blk = ret = allocTermChunk(len); 00391 mTermIndex.ref->write(where+CHUNK_HEADER_NEXT, blk); 00392 mTermIndex.ref->write(blk+CHUNK_HEADER_COUNT, len); 00393 mTermIndex.ref->write(blk+CHUNK_HEADER_USED, CHUNK_HEADER_end + 1); 00394 mTermIndex.ref->write(blk+CHUNK_HEADER_end, serNum); 00395 } else { 00396 mTermIndex.ref->write(where+ 00397 mTermIndex.ref->read(where+CHUNK_HEADER_USED), serNum); 00398 mTermIndex.ref->write(where+CHUNK_HEADER_USED, 00399 mTermIndex.ref->read(where+CHUNK_HEADER_USED)+1); 00400 } 00401 } 00402 return ret; 00403 } 00404 00405 // adds the body term to the index 00406 bool 00407 KMMsgIndex::addBodyTerm(const char *term, uchar term_len, Q_UINT32 serNum) 00408 { 00409 if(mTermIndex.ref->error()) 00410 return FALSE; 00411 if(isKillTerm(term, term_len)) 00412 return TRUE; //sorta.. 00413 if(mIndexState == INDEX_RESTORE) //just have to finish reading.. 00414 restoreState(); 00415 00416 if(!mTermTOC.body.contains(term)) { 00417 int w = addBucket(-1, serNum); 00418 mTermTOC.body.insert(term, w); //mark in TOC 00419 const uchar marker = TOC_BODY; 00420 write(mTermTOC.fd, &marker, sizeof(marker)); 00421 write(mTermTOC.fd, &term_len, sizeof(term_len)); 00422 write(mTermTOC.fd, term, term_len); 00423 write(mTermTOC.fd, &w, sizeof(w)); 00424 } else { 00425 int map_off = mTermTOC.body[term], 00426 w = addBucket(mTermIndex.ref->read(map_off), serNum); 00427 if(w != -1) 00428 mTermIndex.ref->write(map_off, w); 00429 } 00430 return TRUE; 00431 } 00432 00433 // adds the body term to the index 00434 bool 00435 KMMsgIndex::addHeaderTerm(Q_UINT16 hnum, const char *term, 00436 uchar term_len, Q_UINT32 serNum) 00437 { 00438 if(mTermIndex.ref->error()) 00439 return FALSE; 00440 if(isKillTerm(term, term_len)) 00441 return TRUE; //sorta.. 00442 if(mIndexState == INDEX_RESTORE) //just have to finish reading.. 00443 restoreState(); 00444 00445 if(!mTermTOC.h.headers.contains(hnum)) 00446 mTermTOC.h.headers.insert(hnum, QMap<QCString, int>()); 00447 if(!mTermTOC.h.headers[hnum].contains(term)) { 00448 int w = addBucket(-1, serNum); 00449 mTermTOC.h.headers[hnum].insert(term, w); 00450 const uchar marker = TOC_HEADER_DATA; 00451 write(mTermTOC.fd, &marker, sizeof(marker)); 00452 write(mTermTOC.fd, &hnum, sizeof(hnum)); 00453 write(mTermTOC.fd, &term_len, sizeof(term_len)); 00454 write(mTermTOC.fd, term, term_len); 00455 write(mTermTOC.fd, &w, sizeof(w)); 00456 } else { 00457 int map_off = mTermTOC.h.headers[hnum][term], 00458 w = addBucket(mTermIndex.ref->read(map_off), serNum); 00459 if(w != -1) 00460 mTermIndex.ref->write(map_off, w); 00461 } 00462 return TRUE; 00463 } 00464 00465 // processes the message at serNum and returns the number of terms processed 00466 int 00467 KMMsgIndex::processMsg(Q_UINT32 serNum) 00468 { 00469 if(mIndexState == INDEX_RESTORE) { 00470 create.serNums.push(serNum); 00471 return -1; 00472 } 00473 if(mTermProcessed.known[serNum]) 00474 return -1; 00475 00476 int idx = -1; 00477 KMFolder *folder = 0; 00478 kmkernel->msgDict()->getLocation(serNum, &folder, &idx); 00479 if(!folder || (idx == -1) || (idx >= folder->count())) 00480 return -1; 00481 if(mOpenedFolders.findIndex(folder) == -1) { 00482 folder->open(); 00483 mOpenedFolders.append(folder); 00484 } 00485 00486 int ret = 0; 00487 bool unget = !folder->getMsgBase(idx)->isMessage(); 00488 KMMessage *msg = folder->getMsg(idx); 00489 const DwMessage *dw_msg = msg->asDwMessage(); 00490 DwHeaders& headers = dw_msg->Headers(); 00491 uchar build_i = 0; 00492 char build_str[255]; 00493 00494 //process header 00495 for(DwField *field = headers.FirstField(); field; field = field->Next()) { 00496 if(isKillHeader(field->FieldNameStr().data(), 00497 field->FieldNameStr().length())) 00498 continue; 00499 const char *name = field->FieldNameStr().c_str(), 00500 *content = field->FieldBodyStr().data(); 00501 uint content_len = field->FieldBodyStr().length(); 00502 Q_UINT16 hnum = 0; 00503 if(mTermTOC.h.header_lookup.contains(name)) { 00504 hnum = mTermTOC.h.header_lookup[name]; 00505 } else { 00506 hnum = mTermTOC.h.next_hnum++; 00507 mTermTOC.h.header_lookup.insert(name, hnum); 00508 const uchar marker = TOC_HEADER_NAME; 00509 write(mTermTOC.fd, &marker, sizeof(marker)); 00510 uchar len = field->FieldNameStr().length(); 00511 write(mTermTOC.fd, &len, sizeof(len)); 00512 write(mTermTOC.fd, name, len); 00513 write(mTermTOC.fd, &hnum, sizeof(hnum)); 00514 } 00515 for(uint i = 0; i < content_len; i++) { 00516 if(build_i < 254 && !km_isSeparator(content, i, content_len)) { 00517 build_str[build_i++] = tolower(content[i]); 00518 } else if(build_i) { 00519 build_str[build_i] = 0; 00520 if(addHeaderTerm(hnum, build_str, build_i, serNum)) 00521 ret++; 00522 build_i = 0; 00523 } 00524 } 00525 if(build_i) { 00526 build_str[build_i] = 0; 00527 if(addHeaderTerm(hnum, build_str, build_i, serNum)) 00528 ret++; 00529 build_i = 0; 00530 } 00531 } 00532 00533 //process body 00534 const DwEntity *dw_ent = msg->asDwMessage(); 00535 DwString dw_body; 00536 DwString body; 00537 if(dw_ent && dw_ent->hasHeaders() && dw_ent->Headers().HasContentType() && 00538 (dw_ent->Headers().ContentType().Type() == DwMime::kTypeText)) { 00539 dw_body = dw_ent->Body().AsString(); 00540 } else { 00541 dw_ent = msg->getFirstDwBodyPart(); 00542 if (dw_ent) 00543 dw_body = msg->getFirstDwBodyPart()->AsString(); 00544 } 00545 if(dw_ent && dw_ent->hasHeaders() && dw_ent->Headers().HasContentType() && 00546 (dw_ent->Headers().ContentType().Type() == DwMime::kTypeText)) { 00547 DwHeaders& headers = dw_ent->Headers(); 00548 if(headers.HasContentTransferEncoding()) { 00549 switch(headers.ContentTransferEncoding().AsEnum()) { 00550 case DwMime::kCteBase64: { 00551 DwString raw_body = dw_body; 00552 DwDecodeBase64(raw_body, body); 00553 break; } 00554 case DwMime::kCteQuotedPrintable: { 00555 DwString raw_body = dw_body; 00556 DwDecodeQuotedPrintable(raw_body, body); 00557 break; } 00558 default: 00559 body = dw_body; 00560 break; 00561 } 00562 } else { 00563 body = dw_body; 00564 } 00565 } 00566 QDict<void> found_terms; 00567 const char *body_s = body.data(); 00568 for(uint i = 0; i < body.length(); i++) { 00569 if(build_i < 254 && !km_isSeparator(body_s, i, body.length())) { 00570 build_str[build_i++] = tolower(body_s[i]); 00571 } else if(build_i) { 00572 build_str[build_i] = 0; 00573 if(!found_terms[build_str] && addBodyTerm(build_str, build_i, 00574 serNum)) { 00575 found_terms.insert(build_str, (void*)1); 00576 ret++; 00577 } 00578 build_i = 0; 00579 } 00580 } 00581 if(build_i) { 00582 build_str[build_i] = 0; 00583 if(!found_terms[build_str] && addBodyTerm(build_str, 00584 build_i, serNum)) { 00585 found_terms.insert(build_str, (void*)1); 00586 ret++; 00587 } 00588 } 00589 if (unget) 00590 folder->unGetMsg(idx); //I don't need it anymore.. 00591 mTermIndex.ref->write(HEADER_INDEXED, ++mTermIndex.indexed); 00592 mTermProcessed.known.insert(serNum, (void*)1); 00593 write(mTermProcessed.fd, &serNum, sizeof(serNum)); 00594 return ret; 00595 } 00596 00597 //Determines if it is time for another cleanup 00598 bool 00599 KMMsgIndex::isTimeForClean() 00600 { 00601 return (mTermIndex.removed > 2500 && //minimum 00602 mTermIndex.removed * 4 >= mTermIndex.indexed && //fraction removed 00603 (mLastSearch.isNull() || //never 00604 mLastSearch.secsTo(QTime::currentTime()) > 60 * 60 * 2)); //hours 00605 } 00606 00607 //removes bogus entries from the index, and optimizes the index file 00608 void 00609 KMMsgIndex::cleanUp() 00610 { 00611 if(mIndexState != INDEX_IDLE) 00612 return; 00613 reset(TRUE); 00614 remove(); 00615 recreateIndex(); 00616 } 00617 00618 // flushes all open file descriptors.. 00619 void 00620 KMMsgIndex::flush() 00621 { 00622 #if 0 00623 mTermIndex.ref->sync(); 00624 sync(); 00625 #endif 00626 } 00627 00628 // slot fired when a serial number is no longer used 00629 void 00630 KMMsgIndex::slotRemoveMsg(KMFolder *, Q_UINT32) 00631 { 00632 mTermIndex.ref->write(HEADER_REMOVED, ++mTermIndex.removed); 00633 } 00634 00635 // slot fired when new serial numbers are allocated.. 00636 void 00637 KMMsgIndex::slotAddMsg(KMFolder *, Q_UINT32 serNum) 00638 { 00639 if(mIndexState == INDEX_CREATE) { 00640 create.serNums.push(serNum); 00641 } else if(isTimeForClean()) { 00642 cleanUp(); 00643 } else { 00644 processMsg(serNum); 00645 flush(); 00646 } 00647 } 00648 00649 // handles the lazy processing of messages 00650 void 00651 KMMsgIndex::timerEvent(QTimerEvent *e) 00652 { 00653 if(qApp->hasPendingEvents()) //nah, some other time.. 00654 delay_cnt = 10; 00655 else if(delay_cnt) 00656 --delay_cnt; 00657 else if(mIndexState == INDEX_CREATE && e->timerId() == create.timer_id) 00658 createState(FALSE); 00659 else if(mIndexState == INDEX_RESTORE && e->timerId() == restore.timer_id) 00660 restoreState(FALSE); 00661 } 00662 00663 bool 00664 KMMsgIndex::createState(bool finish) 00665 { 00666 int terms = 0, processed = 0, skipped = 0; 00667 const int max_terms = 300, max_process = 30; 00668 while(!create.serNums.isEmpty()) { 00669 if(!finish && 00670 (terms >= max_terms || processed >= max_process || 00671 skipped >= (max_process*4))) { 00672 flush(); 00673 return TRUE; 00674 } 00675 int cnt = processMsg(create.serNums.pop()); 00676 if(cnt == -1) { 00677 skipped++; 00678 } else { 00679 terms += cnt; 00680 processed++; 00681 } 00682 } 00683 if(KMFolder *f = create.folders.pop()) { 00684 if(mOpenedFolders.findIndex(f) == -1) { 00685 f->open(); 00686 mOpenedFolders.append(f); 00687 } 00688 for(int i = 0, s; i < f->count(); ++i) { 00689 s = kmkernel->msgDict()->getMsgSerNum(f, i); 00690 if(finish || 00691 (terms < max_terms && processed < max_process && 00692 skipped < (max_process*4))) { 00693 int cnt = processMsg(s); 00694 if(cnt == -1) { 00695 skipped++; 00696 } else { 00697 terms += cnt; 00698 processed++; 00699 } 00700 } else if(!mTermProcessed.known[s]){ 00701 create.serNums.push(s); 00702 } 00703 } 00704 if(finish) { 00705 while(!createState(TRUE)); 00706 return TRUE; 00707 } 00708 } else { 00709 mIndexState = INDEX_IDLE; 00710 killTimer(create.timer_id); 00711 create.timer_id = -1; 00712 QValueListConstIterator<QGuardedPtr<KMFolder> > it; 00713 for (it = mOpenedFolders.begin(); 00714 it != mOpenedFolders.end(); ++it) { 00715 KMFolder *folder = *it; 00716 if(folder) 00717 folder->close(); 00718 } 00719 mOpenedFolders.clear(); 00720 create.folders.clear(); 00721 mTermIndex.ref->write(HEADER_COMPLETE, 1); 00722 return TRUE; 00723 } 00724 flush(); 00725 return FALSE; 00726 } 00727 00728 // reads in some terms from the index (non-blocking) if finish is true it 00729 // will read in everything left to do. It is possible for this to turn from the 00730 // RESTORE state into the CREATE state - so you must handle this case if you 00731 // need to use the index immediately (ie finish is true) 00732 bool 00733 KMMsgIndex::restoreState(bool finish) { 00734 if(mIndexState != INDEX_RESTORE) 00735 return FALSE; 00736 uchar marker, len; 00737 char in[255]; 00738 Q_UINT32 off; 00739 for(int cnt = 0; finish || cnt < 400; cnt++) { 00740 if(restore.reading_processed) { 00741 Q_UINT32 ser; 00742 if(!read(mTermProcessed.fd, &ser, sizeof(ser))) { 00743 mIndexState = INDEX_IDLE; 00744 killTimer(restore.timer_id); 00745 restore.timer_id = -1; 00746 if(restore.restart_index) { 00747 mIndexState = INDEX_CREATE; 00748 syncIndex(); 00749 } 00750 break; 00751 } 00752 mTermProcessed.known.insert(ser, (void*)1); 00753 } else { 00754 if(!read(mTermTOC.fd, &marker, sizeof(marker))) 00755 restore.reading_processed = TRUE; 00756 if(marker == TOC_BODY) { 00757 read(mTermTOC.fd, &len, sizeof(len)); 00758 read(mTermTOC.fd, in, len); 00759 in[len] = 0; 00760 read(mTermTOC.fd, &off, sizeof(off)); 00761 mTermTOC.body.insert(in, off); 00762 } else if(marker == TOC_HEADER_DATA) { 00763 Q_UINT16 hnum; 00764 read(mTermTOC.fd, &hnum, sizeof(hnum)); 00765 read(mTermTOC.fd, &len, sizeof(len)); 00766 read(mTermTOC.fd, in, len); 00767 in[len] = 0; 00768 read(mTermTOC.fd, &off, sizeof(off)); 00769 if(!mTermTOC.h.headers.contains(hnum)) { 00770 QMap<QCString, int> map; 00771 map.insert(in, off); 00772 mTermTOC.h.headers.insert(hnum, map); 00773 } else { 00774 mTermTOC.h.headers[hnum].insert(in, off); 00775 } 00776 } else if(marker == TOC_HEADER_NAME) { 00777 read(mTermTOC.fd, &len, sizeof(len)); 00778 read(mTermTOC.fd, in, len); 00779 in[len] = 0; 00780 Q_UINT16 hnum; 00781 read(mTermTOC.fd, &hnum, sizeof(hnum)); 00782 if(!mTermTOC.h.header_lookup.contains(in)) { 00783 mTermTOC.h.header_lookup.insert(in, hnum); 00784 mTermTOC.h.next_hnum = hnum + 1; 00785 } 00786 } 00787 } 00788 } 00789 return TRUE; 00790 } 00791 00792 // nulls the current index and begins a refresh of the indexed data.. 00793 bool 00794 KMMsgIndex::recreateIndex() 00795 { 00796 if(mIndexState != INDEX_IDLE) 00797 return FALSE; 00798 00799 mIndexState = INDEX_CREATE; 00800 mTermProcessed.fd = open(mTermProcessed.loc.latin1(), 00801 O_WRONLY|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE); 00802 mTermTOC.fd = open(mTermTOC.loc.latin1(), 00803 O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE); 00804 mTermIndex.fd = open(mTermIndex.loc.latin1(), 00805 O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE); 00806 mTermIndex.count = kmindex_grow_increment; 00807 mTermIndex.used = HEADER_end; 00808 mTermIndex.ref = new KMMsgIndexRef(mTermIndex.fd, 0); 00809 mTermIndex.ref->resize(mTermIndex.count); 00810 mTermIndex.ref->write(HEADER_BYTEORDER, 0x12345678); 00811 mTermIndex.ref->write(HEADER_VERSION, KMMSGINDEX_VERSION); 00812 mTermIndex.ref->write(HEADER_COMPLETE, 0); //marker for incomplete index 00813 mTermIndex.ref->write(HEADER_COUNT, mTermIndex.count); 00814 mTermIndex.ref->write(HEADER_USED, mTermIndex.used);//including this header 00815 mTermIndex.ref->write(HEADER_INDEXED, mTermIndex.indexed); 00816 mTermIndex.ref->write(HEADER_REMOVED, mTermIndex.removed); 00817 syncIndex(); 00818 return TRUE; 00819 } 00820 00821 // processes all current messages as if they were newly added 00822 void 00823 KMMsgIndex::syncIndex() 00824 { 00825 if(mIndexState != INDEX_CREATE) 00826 return; 00827 QValueStack<QGuardedPtr<KMFolderDir> > folders; 00828 folders.push(&(kmkernel->folderMgr()->dir())); 00829 while(KMFolderDir *dir = folders.pop()) { 00830 for(KMFolderNode *child = dir->first(); child; child = dir->next()) { 00831 if(child->isDir()) 00832 folders.push((KMFolderDir*)child); 00833 else 00834 create.folders.push((KMFolder*)child); 00835 } 00836 } 00837 if(create.timer_id == -1) 00838 create.timer_id = startTimer(0); 00839 } 00840 00841 // read the existing index and load into memory 00842 void 00843 KMMsgIndex::readIndex() 00844 { 00845 if(mIndexState != INDEX_IDLE) 00846 return; 00847 mIndexState = INDEX_RESTORE; 00848 bool read_success = FALSE; 00849 if((mTermTOC.fd = open(mTermTOC.loc.latin1(), O_RDWR)) != -1) { 00850 if((mTermIndex.fd = open(mTermIndex.loc.latin1(), O_RDWR)) != -1) { 00851 mTermProcessed.fd = open(mTermProcessed.loc.latin1(), O_RDWR); 00852 00853 Q_INT32 byteOrder = 0, version; 00854 read(mTermIndex.fd, &byteOrder, sizeof(byteOrder)); 00855 if(byteOrder != 0x12345678) 00856 goto error_with_read; 00857 read(mTermIndex.fd, &version, sizeof(version)); 00858 if(version != KMMSGINDEX_VERSION) 00859 goto error_with_read; 00860 Q_UINT32 complete_index = 0; 00861 read(mTermIndex.fd, &complete_index, sizeof(complete_index)); 00862 restore.restart_index = !complete_index; 00863 read(mTermIndex.fd, &mTermIndex.count, sizeof(mTermIndex.count)); 00864 read(mTermIndex.fd, &mTermIndex.used, sizeof(mTermIndex.used)); 00865 read(mTermIndex.fd, &mTermIndex.indexed, 00866 sizeof(mTermIndex.indexed)); 00867 read(mTermIndex.fd, &mTermIndex.removed, 00868 sizeof(mTermIndex.removed)); 00869 mTermIndex.ref = new KMMsgIndexRef(mTermIndex.fd, 00870 mTermIndex.count); 00871 if(mTermIndex.ref->error()) 00872 goto error_with_read; 00873 restore.timer_id = startTimer(0); 00874 read_success = TRUE; 00875 } 00876 } 00877 error_with_read: 00878 if(!read_success) { 00879 mIndexState = INDEX_IDLE; 00880 reset(); 00881 remove(); 00882 recreateIndex(); 00883 } 00884 } 00885 00886 // returns whether rule is a valid rule to be processed by the indexer 00887 bool 00888 KMMsgIndex::canHandleQuery(KMSearchRule *rule) 00889 { 00890 if(mIndexState == INDEX_RESTORE) //just have to finish reading.. 00891 restoreState(); //this might flip us into INDEX_CREATE state.. 00892 if(mIndexState != INDEX_IDLE) //not while we are doing other stuff.. 00893 return FALSE; 00894 if(rule->field().isEmpty() || rule->contents().isEmpty()) //not a real search 00895 return TRUE; 00896 if(rule->function() != KMSearchRule::FuncEquals && 00897 rule->function() != KMSearchRule::FuncContains) { 00898 return FALSE; 00899 } else if(rule->field().left(1) == "<") { 00900 if(rule->field() == "<body>" || rule->field() == "<message>") { 00901 if(rule->function() != KMSearchRule::FuncContains) 00902 return FALSE; 00903 } else if(rule->field() != "<recipients>") { //unknown.. 00904 return FALSE; 00905 } 00906 } else if(isKillHeader(rule->field().data(), rule->field().length())) { 00907 return FALSE; 00908 } 00909 QString match = rule->contents().lower(); //general case 00910 if(km_isSeparated(match)) { 00911 uint killed = 0; 00912 QStringList sl = km_separate(match); 00913 for(QStringList::Iterator it = sl.begin(); 00914 it != sl.end(); ++it) { 00915 QString str = (*it).lower(); 00916 if(isKillTerm(str.latin1(), str.length())) 00917 killed++; 00918 } 00919 if(killed == sl.count()) 00920 return FALSE; 00921 } else if(isKillTerm(match.latin1(), match.length())) { 00922 return FALSE; 00923 } 00924 return TRUE; 00925 } 00926 00927 // returns whether pat is a valid pattern to be processed by the indexer 00928 bool 00929 KMMsgIndex::canHandleQuery(KMSearchPattern *pat) 00930 { 00931 if(mIndexState == INDEX_RESTORE) //just have to finish reading.. 00932 restoreState(); //this might flip us into INDEX_CREATE state.. 00933 if(mIndexState != INDEX_IDLE) //not while we are creating the index.. 00934 return FALSE; 00935 if(pat->op() != KMSearchPattern::OpAnd && 00936 pat->op() != KMSearchPattern::OpOr) 00937 return FALSE; 00938 for(QPtrListIterator<KMSearchRule> it(*pat); it.current(); ++it) { 00939 if(!canHandleQuery((*it))) 00940 return FALSE; 00941 } 00942 return TRUE; 00943 } 00944 00945 // returns the data set at begin_chunk through to end_chunk 00946 void 00947 KMMsgIndex::values(int begin_chunk, int end_chunk, QValueList<Q_UINT32> &lst) 00948 { 00949 lst.clear(); 00950 for(int off = begin_chunk; TRUE; 00951 off = mTermIndex.ref->read(off+CHUNK_HEADER_NEXT)) { 00952 uint used = mTermIndex.ref->read(off+CHUNK_HEADER_USED); 00953 for(uint i = CHUNK_HEADER_end; i < used; i++) 00954 lst << mTermIndex.ref->read(off+i); 00955 if(mTermIndex.ref->read(off) != used || off == end_chunk) 00956 break; 00957 } 00958 } 00959 00960 // returns the data set at begin_chunk through to end_chunk 00961 void 00962 KMMsgIndex::values(int begin_chunk, int end_chunk, QIntDict<void> &dct) 00963 { 00964 dct.clear(); 00965 for(int off = begin_chunk; TRUE; 00966 off = mTermIndex.ref->read(off+CHUNK_HEADER_NEXT)) { 00967 uint used = mTermIndex.ref->read(off+CHUNK_HEADER_USED); 00968 for(uint i = CHUNK_HEADER_end; i < used; i++) 00969 dct.insert(mTermIndex.ref->read(off+i), (void*)1); 00970 if(mTermIndex.ref->read(off) != used || off == end_chunk) 00971 break; 00972 } 00973 } 00974 00975 // performs an actual search in the index 00976 QValueList<Q_UINT32> 00977 KMMsgIndex::find(QString data, bool contains, KMSearchRule *rule, 00978 bool body, Q_UINT16 hnum) 00979 { 00980 QValueList<Q_UINT32> ret; 00981 if(!body && !mTermTOC.h.headers.contains(hnum)) 00982 return ret; 00983 if(contains) { 00984 QIntDict<void> foundDict; 00985 QMap<QCString, int> *map = &(mTermTOC.body); 00986 if(!body) 00987 map = &(mTermTOC.h.headers[hnum]); 00988 QStringList sl = km_separate(data); 00989 for(QStringList::Iterator slit = sl.begin(); 00990 slit != sl.end(); ++slit) { 00991 for(QMapIterator<QCString, int> it = map->begin(); 00992 it != map->end(); ++it) { 00993 QString qstr = it.key(); 00994 bool matches = FALSE; 00995 if(sl.count() == 1) 00996 matches = qstr.contains((*slit)); 00997 else if(slit == sl.begin()) 00998 matches = qstr.endsWith((*slit)); 00999 else if(slit == sl.end()) 01000 matches = qstr.startsWith((*slit)); 01001 else 01002 matches = (qstr == (*slit)); 01003 if(matches) { 01004 QValueList<Q_UINT32> tmp = find(it.key(), FALSE, rule, 01005 body, hnum); 01006 for(QValueListIterator<Q_UINT32> tmp_it = tmp.begin(); 01007 tmp_it != tmp.end(); ++tmp_it) { 01008 if(!foundDict[(*tmp_it)]) 01009 foundDict.insert((*tmp_it), (void*)1); 01010 } 01011 } 01012 } 01013 } 01014 for(QIntDictIterator<void> it(foundDict); it.current(); ++it) 01015 ret << it.currentKey(); 01016 return ret; 01017 } 01018 mLastSearch = QTime::currentTime(); 01019 01020 bool exhaustive_search = FALSE; 01021 if(km_isSeparated(data)) { //phrase search.. 01022 bool first = TRUE; 01023 QIntDict<void> foundDict; 01024 QStringList sl = km_separate(data); 01025 for(QStringList::Iterator it = sl.begin(); it != sl.end(); ++it) { 01026 if(!isKillTerm((*it).latin1(), (*it).length())) { 01027 QCString cstr((*it).latin1()); 01028 int map_off = 0; 01029 if(body) { 01030 if(!mTermTOC.body.contains(cstr)) 01031 return ret; 01032 map_off = mTermTOC.body[cstr]; 01033 } else { 01034 if(!mTermTOC.h.headers[hnum].contains(cstr)) 01035 return ret; 01036 map_off = mTermTOC.h.headers[hnum][cstr]; 01037 } 01038 if(first) { 01039 first = FALSE; 01040 values(map_off+1, mTermIndex.ref->read(map_off), 01041 foundDict); 01042 } else { 01043 QIntDict<void> andDict; 01044 QValueList<Q_UINT32> tmp; 01045 values(map_off+1, mTermIndex.ref->read(map_off), tmp); 01046 for(QValueListIterator<Q_UINT32> it = tmp.begin(); 01047 it != tmp.end(); ++it) { 01048 if(foundDict[(*it)]) 01049 andDict.insert((*it), (void*)1); 01050 } 01051 foundDict = andDict; 01052 } 01053 } 01054 } 01055 for(QIntDictIterator<void> it(foundDict); it.current(); ++it) 01056 ret << it.currentKey(); 01057 exhaustive_search = TRUE; 01058 } else if(!isKillTerm(data.latin1(), data.length())) { 01059 QCString cstr(data.latin1()); 01060 int map_off = -1; 01061 if(body) { 01062 if(mTermTOC.body.contains(cstr)) 01063 map_off = mTermTOC.body[cstr]; 01064 } else { 01065 if(mTermTOC.h.headers[hnum].contains(cstr)) 01066 map_off = mTermTOC.h.headers[hnum][cstr]; 01067 } 01068 if(map_off != -1) 01069 values(map_off+1, mTermIndex.ref->read(map_off), ret); 01070 } 01071 if(!ret.isEmpty() && rule && 01072 (exhaustive_search || rule->function() == KMSearchRule::FuncEquals)) { 01073 QValueList<Q_UINT32> tmp; 01074 for(QValueListIterator<Q_UINT32> it = ret.begin(); 01075 it != ret.end(); ++it) { 01076 int idx = -1, ser = (*it); 01077 KMFolder *folder = 0; 01078 kmkernel->msgDict()->getLocation(ser, &folder, &idx); 01079 if(!folder || (idx == -1)) 01080 continue; 01081 KMMessage *msg = folder->getMsg(idx); 01082 if(rule->matches(msg)) 01083 tmp << ser; 01084 } 01085 return tmp; 01086 } 01087 return ret; 01088 } 01089 01090 01091 // processes rule and performs the indexed look up, if exhaustive_search 01092 // is true it will interpret body() as a full phrase rather than AND'd set 01093 QValueList<Q_UINT32> 01094 KMMsgIndex::query(KMSearchRule *rule, bool exhaustive_search) 01095 { 01096 if(!canHandleQuery(rule) || rule->field().isEmpty() || rule->contents().isEmpty()) 01097 return QValueList<Q_UINT32>(); 01098 if(rule->field().left(1) == "<") { 01099 if((rule->field() == "<body>" || rule->field() == "<message>") && 01100 rule->function() == KMSearchRule::FuncContains) { 01101 return find(rule->contents().lower(), 01102 TRUE, exhaustive_search ? rule : NULL, TRUE); 01103 } else if(rule->field() == "<recipients>") { 01104 bool first = TRUE; 01105 QIntDict<void> foundDict; 01106 QString contents = rule->contents().lower(); 01107 const char *hdrs[] = { "To", "CC", "BCC", NULL }; 01108 for(int i = 0; hdrs[i]; i++) { 01109 int l = strlen(hdrs[i]); 01110 if(isKillHeader(hdrs[i], l)) //can't really happen 01111 continue; 01112 QValueList<Q_UINT32> tmp = find(contents, 01113 rule->function() == KMSearchRule::FuncContains, 01114 exhaustive_search ? rule : NULL, FALSE, 01115 mTermTOC.h.header_lookup[hdrs[i]]); 01116 if(first) { 01117 first = FALSE; 01118 for(QValueListIterator<Q_UINT32> it = tmp.begin(); 01119 it != tmp.end(); ++it) 01120 foundDict.insert((*it), (void*)1); 01121 } else { 01122 for(QValueListIterator<Q_UINT32> it = tmp.begin(); 01123 it != tmp.end(); ++it) { 01124 if(!foundDict[(*it)]) 01125 foundDict.insert((*it), (void*)1); 01126 } 01127 } 01128 } 01129 QValueList<Q_UINT32> ret; 01130 for(QIntDictIterator<void> it(foundDict); it.current(); ++it) 01131 ret << it.currentKey(); 01132 return ret; 01133 } 01134 return QValueList<Q_UINT32>(); //can't really happen.. 01135 } 01136 //general header case.. 01137 if(!mTermTOC.h.header_lookup.contains(rule->field())) 01138 return QValueList<Q_UINT32>(); 01139 return find(rule->contents().lower(), 01140 rule->function() == KMSearchRule::FuncContains, 01141 exhaustive_search ? rule : NULL, FALSE, 01142 mTermTOC.h.header_lookup[rule->field()]); 01143 01144 } 01145 01146 // processes rule and performs the indexed look up, if exhaustive_search 01147 // is true it will interpret body()[s] as full phrases rather than AND'd sets 01148 QValueList<Q_UINT32> 01149 KMMsgIndex::query(KMSearchPattern *pat, bool exhaustive_search) 01150 { 01151 QValueList<Q_UINT32> ret; 01152 if(pat->isEmpty() || !canHandleQuery(pat)) 01153 return ret; 01154 01155 if(pat->count() == 1) { 01156 ret = query(pat->first(), exhaustive_search); 01157 } else { 01158 bool first = TRUE; 01159 QIntDict<void> foundDict; 01160 for(QPtrListIterator<KMSearchRule> it(*pat); it.current(); ++it) { 01161 if((*it)->field().isEmpty() || (*it)->contents().isEmpty()) 01162 continue; 01163 QValueList<Q_UINT32> tmp = query((*it), exhaustive_search); 01164 if(first) { 01165 first = FALSE; 01166 for(QValueListIterator<Q_UINT32> it = tmp.begin(); 01167 it != tmp.end(); ++it) 01168 foundDict.insert((long int)(*it), (void*)1); 01169 } else { 01170 if(pat->op() == KMSearchPattern::OpAnd) { 01171 QIntDict<void> andDict; 01172 for(QValueListIterator<Q_UINT32> it = tmp.begin(); 01173 it != tmp.end(); ++it) { 01174 if(foundDict[(*it)]) 01175 andDict.insert((*it), (void*)1); 01176 } 01177 foundDict = andDict; 01178 } else if(pat->op() == KMSearchPattern::OpOr) { 01179 for(QValueListIterator<Q_UINT32> it = tmp.begin(); 01180 it != tmp.end(); ++it) { 01181 if(!foundDict[(*it)]) 01182 foundDict.insert((long int)(*it), (void*)1); 01183 } 01184 } 01185 } 01186 } 01187 for(QIntDictIterator<void> it(foundDict); it.current(); ++it) 01188 ret << it.currentKey(); 01189 } 01190 return ret; 01191 } 01192 01193 // Code to bind to a KMSearch 01194 KMIndexSearchTarget::KMIndexSearchTarget(KMSearch *s) : QObject(NULL, NULL), 01195 mVerifyResult(FALSE) 01196 { 01197 mSearch = s; 01198 mId = startTimer(0); 01199 { 01200 QValueList<Q_UINT32> lst = kmkernel->msgIndex()->query( 01201 s->searchPattern(), FALSE); 01202 for(QValueListConstIterator<Q_UINT32> it = lst.begin(); 01203 it != lst.end(); ++it) 01204 mSearchResult.push((*it)); 01205 } 01206 for(QPtrListIterator<KMSearchRule> it(*s->searchPattern()); 01207 it.current(); ++it) { 01208 if((*it)->function() != KMSearchRule::FuncContains || 01209 km_isSeparated((*it)->contents())) { 01210 mVerifyResult = TRUE; 01211 break; 01212 } 01213 } 01214 QObject::connect(this, SIGNAL(proxyFound(Q_UINT32)), 01215 s, SIGNAL(found(Q_UINT32))); 01216 QObject::connect(this, SIGNAL(proxyFinished(bool)), 01217 s, SIGNAL(finished(bool))); 01218 } 01219 KMIndexSearchTarget::~KMIndexSearchTarget() 01220 { 01221 stop(); 01222 QValueListConstIterator<QGuardedPtr<KMFolder> > it; 01223 for (it = mOpenedFolders.begin(); it != mOpenedFolders.end(); ++it) { 01224 KMFolder *folder = *it; 01225 if(folder) 01226 folder->close(); 01227 } 01228 mOpenedFolders.clear(); 01229 } 01230 void 01231 KMIndexSearchTarget::timerEvent(QTimerEvent *) 01232 { 01233 if(qApp->hasPendingEvents()) 01234 return; //no time now 01235 bool finished = FALSE; 01236 if(mSearch) { 01237 KMFolder *folder; 01238 const uint max_src = mVerifyResult ? 100 : 500; 01239 int stop_at = QMIN(mSearchResult.count(), max_src); 01240 for(int i = 0, idx; i < stop_at; i++) { 01241 Q_UINT32 serNum = mSearchResult.pop(); 01242 kmkernel->msgDict()->getLocation(serNum, &folder, &idx); 01243 if (!folder || (idx == -1)) 01244 continue; 01245 if(mSearch->inScope(folder)) { 01246 mSearch->setSearchedCount(mSearch->searchedCount()+1); 01247 mSearch->setCurrentFolder(folder->label()); 01248 if(mVerifyResult) { //full phrase.. 01249 if(mOpenedFolders.findIndex(folder) == -1) { 01250 folder->open(); 01251 mOpenedFolders.append(folder); 01252 } 01253 if(!mSearch->searchPattern()->matches( 01254 folder->getDwString(idx))) 01255 continue; 01256 } 01257 mSearch->setFoundCount(mSearch->foundCount()+1); 01258 emit proxyFound(serNum); 01259 } 01260 } 01261 if(mSearchResult.isEmpty()) 01262 finished = TRUE; 01263 } else { 01264 finished = TRUE; 01265 } 01266 if(finished) { 01267 if(mSearch && mSearch->running()) 01268 mSearch->setRunning(FALSE); 01269 stop(TRUE); 01270 killTimer(mId); 01271 kmkernel->msgIndex()->stopQuery(id()); 01272 } 01274 } 01275 bool 01276 KMMsgIndex::startQuery(KMSearch *s) 01277 { 01278 if(!canHandleQuery(s->searchPattern())) 01279 return FALSE; 01280 KMIndexSearchTarget *targ = new KMIndexSearchTarget(s); 01281 mActiveSearches.insert(targ->id(), targ); 01282 return TRUE; 01283 } 01284 bool 01285 KMMsgIndex::stopQuery(KMSearch *s) 01286 { 01287 int id = -1; 01288 for(QIntDictIterator<KMIndexSearchTarget> it(mActiveSearches); 01289 it.current(); ++it) { 01290 if(it.current()->search() == s) { 01291 it.current()->stop(FALSE); 01292 id = it.currentKey(); 01293 break; 01294 } 01295 } 01296 if(id == -1) 01297 return FALSE; 01298 return stopQuery(id); 01299 } 01300 #include "kmmsgindex.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:03 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003