00001
00002
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
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;
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
00241
00242
void
00243 KMMsgIndex::reset(
bool clean)
00244 {
00245
00246
if(clean)
00247 mActiveSearches.clear();
00248
00249
if(create.timer_id != -1) {
00250
if(clean)
00251 killTimer(create.timer_id);
00252 create.timer_id = -1;
00253 }
00254
00255
if(restore.timer_id != -1) {
00256
if(clean)
00257 killTimer(restore.timer_id);
00258 restore.timer_id = -1;
00259 }
00260
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
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
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
00298
int
00299 KMMsgIndex::allocTermChunk(
int cnt)
00300 {
00301
int ret = mTermIndex.used;
00302 mTermIndex.used += cnt;
00303
if(mTermIndex.count < mTermIndex.used) {
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
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
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)
00335
return TRUE;
00336 {
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 {
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
00368
int
00369 KMMsgIndex::addBucket(
int where, Q_UINT32 serNum)
00370 {
00371
int ret = where;
00372
if(where == -1) {
00373
00374
int first_chunk_size = CHUNK_HEADER_end + 2 + 1;
00375
int off = ret = allocTermChunk(first_chunk_size);
00376
00377
00378 mTermIndex.ref->write(off, off+1);
00379 off++;
00380 first_chunk_size--;
00381
00382
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;
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
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;
00413
if(mIndexState == INDEX_RESTORE)
00414 restoreState();
00415
00416
if(!mTermTOC.body.contains(term)) {
00417
int w = addBucket(-1, serNum);
00418 mTermTOC.body.insert(term, w);
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
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;
00442
if(mIndexState == INDEX_RESTORE)
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
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
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
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);
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
00598
bool
00599 KMMsgIndex::isTimeForClean()
00600 {
00601
return (mTermIndex.removed > 2500 &&
00602 mTermIndex.removed * 4 >= mTermIndex.indexed &&
00603 (mLastSearch.isNull() ||
00604 mLastSearch.secsTo(QTime::currentTime()) > 60 * 60 * 2));
00605 }
00606
00607
00608
void
00609 KMMsgIndex::cleanUp()
00610 {
00611
if(mIndexState != INDEX_IDLE)
00612
return;
00613 reset(TRUE);
00614 remove();
00615 recreateIndex();
00616 }
00617
00618
00619
void
00620 KMMsgIndex::flush()
00621 {
00622
#if 0
00623
mTermIndex.ref->sync();
00624 sync();
00625
#endif
00626
}
00627
00628
00629
void
00630 KMMsgIndex::slotRemoveMsg(
KMFolder *, Q_UINT32)
00631 {
00632 mTermIndex.ref->write(HEADER_REMOVED, ++mTermIndex.removed);
00633 }
00634
00635
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
00650
void
00651 KMMsgIndex::timerEvent(QTimerEvent *e)
00652 {
00653
if(qApp->hasPendingEvents())
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
00729
00730
00731
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
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);
00813 mTermIndex.ref->write(HEADER_COUNT, mTermIndex.count);
00814 mTermIndex.ref->write(HEADER_USED, mTermIndex.used);
00815 mTermIndex.ref->write(HEADER_INDEXED, mTermIndex.indexed);
00816 mTermIndex.ref->write(HEADER_REMOVED, mTermIndex.removed);
00817 syncIndex();
00818
return TRUE;
00819 }
00820
00821
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
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
00887
bool
00888 KMMsgIndex::canHandleQuery(
KMSearchRule *rule)
00889 {
00890
if(mIndexState == INDEX_RESTORE)
00891 restoreState();
00892
if(mIndexState != INDEX_IDLE)
00893
return FALSE;
00894
if(rule->
field().isEmpty() || rule->
contents().isEmpty())
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>") {
00904
return FALSE;
00905 }
00906 }
else if(isKillHeader(rule->
field().data(), rule->
field().length())) {
00907
return FALSE;
00908 }
00909 QString match = rule->
contents().lower();
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
00928
bool
00929 KMMsgIndex::canHandleQuery(
KMSearchPattern *pat)
00930 {
00931
if(mIndexState == INDEX_RESTORE)
00932 restoreState();
00933
if(mIndexState != INDEX_IDLE)
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
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
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
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)) {
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
01092
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))
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>();
01135 }
01136
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
01147
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
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;
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) {
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"