00001
00002
00003
00004
00005
#include <config.h>
00006
#include <qfileinfo.h>
00007
#include <qregexp.h>
00008
00009
#include "kmfoldermbox.h"
00010
#include "kmfoldermgr.h"
00011
#include "undostack.h"
00012
#include "kcursorsaver.h"
00013
00014
#include <kdebug.h>
00015
#include <klocale.h>
00016
#include <kmessagebox.h>
00017
#include <knotifyclient.h>
00018
#include <kprocess.h>
00019
#include <kconfig.h>
00020
00021
#include <stdio.h>
00022
#include <errno.h>
00023
#include <assert.h>
00024
#include <unistd.h>
00025
00026
#ifdef HAVE_FCNTL_H
00027
#include <fcntl.h>
00028
#endif
00029
00030
#include <stdlib.h>
00031
#include <sys/types.h>
00032
#include <sys/stat.h>
00033
#include <sys/file.h>
00034
00035
#ifndef MAX_LINE
00036
#define MAX_LINE 4096
00037
#endif
00038
#ifndef INIT_MSGS
00039
#define INIT_MSGS 8
00040
#endif
00041
00042
00043
00044
#define MSG_SEPERATOR_START "From "
00045
#define MSG_SEPERATOR_REGEX "^From .*[0-9][0-9]:[0-9][0-9].*$"
00046
static short msgSepLen = strlen(MSG_SEPERATOR_START);
00047
00048
00049
00050 KMFolderMbox::KMFolderMbox(
KMFolderDir* aParent,
const QString& aName)
00051 : KMFolderIndex(aParent, aName)
00052 {
00053 mStream = 0;
00054 mFilesLocked =
false;
00055 mLockType = lock_none;
00056 }
00057
00058
00059
00060 KMFolderMbox::~KMFolderMbox()
00061 {
00062
if (mOpenCount>0) close(
true);
00063
if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed(
this);
00064 }
00065
00066
00067
int KMFolderMbox::open()
00068 {
00069
int rc = 0;
00070
00071 mOpenCount++;
00072
if (mOpenCount > 1)
return 0;
00073
00074 assert(!name().isEmpty());
00075
00076 mFilesLocked =
false;
00077 mStream = fopen(QFile::encodeName(location()),
"r+");
00078
if (!mStream)
00079 {
00080 KNotifyClient::event( 0,
"warning",
00081 i18n(
"Cannot open file \"%1\":\n%2").arg(location()).arg(strerror(errno)));
00082 kdDebug(5006) <<
"Cannot open folder `" << location() <<
"': " << strerror(errno) << endl;
00083 mOpenCount = 0;
00084
return errno;
00085 }
00086
00087 lock();
00088
00089
if (!
path().isEmpty())
00090 {
00091 KMFolderIndex::IndexStatus index_status = indexStatus();
00092
00093
if (KMFolderIndex::IndexOk != index_status)
00094 {
00095
00096
00097
if (KMFolderIndex::IndexTooOld == index_status) {
00098 QString msg = i18n(
"<qt><p>The index of folder '%2' seems "
00099
"to be out of date. To prevent message "
00100
"corruption the index will be "
00101
"regenerated. As a result deleted "
00102
"messages might reappear and status "
00103
"flags might be lost.</p>"
00104
"<p>Please read the corresponding entry "
00105
"in the <a href=\"%1\">FAQ section of the manual "
00106
"of KMail</a> for "
00107
"information about how to prevent this "
00108
"problem from happening again.</p></qt>")
00109 .arg(
"help:/kmail/faq.html#faq-index-regeneration")
00110 .arg(name());
00111
00112
00113
00114
00115
if (kmkernel->startingUp())
00116 {
00117 KConfigGroup configGroup( KMKernel::config(),
"Notification Messages" );
00118
bool showMessage =
00119 configGroup.readBoolEntry(
"showIndexRegenerationMessage",
true );
00120
if (showMessage)
00121 KMessageBox::queuedMessageBox( 0, KMessageBox::Information,
00122 msg, i18n(
"Index Out of Date"),
00123 KMessageBox::AllowLink );
00124 }
00125
else
00126 {
00127
KCursorSaver idle(KBusyPtr::idle());
00128 KMessageBox::information( 0, msg, i18n(
"Index Out of Date"),
00129
"showIndexRegenerationMessage",
00130 KMessageBox::AllowLink );
00131 }
00132 }
00133 QString str;
00134 mIndexStream = 0;
00135 str = i18n(
"Folder `%1' changed. Recreating index.")
00136 .arg(name());
00137 emit statusMsg(str);
00138 }
else {
00139 mIndexStream = fopen(QFile::encodeName(indexLocation()),
"r+");
00140 updateIndexStreamPtr();
00141 }
00142
00143
if (!mIndexStream)
00144 rc = createIndexFromContents();
00145
else
00146
if (!readIndex())
00147 rc = createIndexFromContents();
00148 }
00149
else
00150 {
00151 mAutoCreateIndex =
false;
00152 rc = createIndexFromContents();
00153 }
00154
00155 mChanged =
false;
00156
00157
return rc;
00158 }
00159
00160
00161
int KMFolderMbox::canAccess()
00162 {
00163 assert(!name().isEmpty());
00164
00165
if (access(QFile::encodeName(location()), R_OK | W_OK) != 0) {
00166 kdDebug(5006) <<
"KMFolderMbox::access call to access function failed" << endl;
00167
return 1;
00168 }
00169
return 0;
00170 }
00171
00172
00173
int KMFolderMbox::create(
bool imap)
00174 {
00175
int rc;
00176
int old_umask;
00177
00178 Q_UNUSED(imap);
00179
00180 assert(!name().isEmpty());
00181 assert(mOpenCount == 0);
00182
00183 kdDebug(5006) <<
"Creating folder " << name() << endl;
00184
if (access(QFile::encodeName(location()), F_OK) == 0) {
00185 kdDebug(5006) <<
"KMFolderMbox::create call to access function failed." << endl;
00186 kdDebug(5006) <<
"File:: " << endl;
00187 kdDebug(5006) <<
"Error " << endl;
00188
return EEXIST;
00189 }
00190
00191 old_umask = umask(077);
00192 mStream = fopen(QFile::encodeName(location()),
"w+");
00193 umask(old_umask);
00194
00195
if (!mStream)
return errno;
00196
00197
if (!
path().isEmpty())
00198 {
00199 old_umask = umask(077);
00200 mIndexStream = fopen(QFile::encodeName(indexLocation()),
"w+");
00201 updateIndexStreamPtr(
true);
00202 umask(old_umask);
00203
00204
if (!mIndexStream)
return errno;
00205 }
00206
else
00207 {
00208 mAutoCreateIndex =
false;
00209 }
00210
00211 mOpenCount++;
00212 mChanged =
false;
00213
00214 rc = writeIndex();
00215
if (!rc) lock();
00216
return rc;
00217 }
00218
00219
00220
00221
void KMFolderMbox::close(
bool aForced)
00222 {
00223
if (mOpenCount <= 0 || !mStream)
return;
00224
if (mOpenCount > 0) mOpenCount--;
00225
if (mOpenCount > 0 && !aForced)
return;
00226
if ((
this != kmkernel->inboxFolder()) && isSystemFolder() && !aForced)
00227 {
00228 mOpenCount = 1;
00229
return;
00230 }
00231
00232
if (mAutoCreateIndex)
00233 {
00234
if (KMFolderIndex::IndexOk != indexStatus()) {
00235 kdDebug(5006) <<
"Critical error: " << location() <<
00236
" has been modified by an external application while KMail was running." << endl;
00237
00238 }
00239
00240 updateIndex();
00241 writeConfig();
00242 }
00243
00244
if (!noContent()) {
00245
if (mStream) unlock();
00246 mMsgList.clear(
true);
00247
00248
if (mStream) fclose(mStream);
00249
if (mIndexStream) {
00250 fclose(mIndexStream);
00251 updateIndexStreamPtr(
true);
00252 }
00253 }
00254 mOpenCount = 0;
00255 mStream = 0;
00256 mIndexStream = 0;
00257 mFilesLocked =
false;
00258 mUnreadMsgs = -1;
00259
00260 mMsgList.reset(INIT_MSGS);
00261 }
00262
00263
00264
void KMFolderMbox::sync()
00265 {
00266
if (mOpenCount > 0)
00267
if (!mStream || fsync(fileno(mStream)) ||
00268 !mIndexStream || fsync(fileno(mIndexStream))) {
00269 kmkernel->emergencyExit( i18n(
"Could not sync index file <b>%1</b>: %2").arg( indexLocation() ).arg(errno ? QString::fromLocal8Bit(strerror(errno)) : i18n(
"Internal error. Please copy down the details and report a bug.")));
00270 }
00271 }
00272
00273
00274
int KMFolderMbox::lock()
00275 {
00276
int rc;
00277
struct flock fl;
00278 fl.l_type=F_WRLCK;
00279 fl.l_whence=0;
00280 fl.l_start=0;
00281 fl.l_len=0;
00282 fl.l_pid=-1;
00283 QCString cmd_str;
00284 assert(mStream != 0);
00285 mFilesLocked =
false;
00286
00287
switch( mLockType )
00288 {
00289
case FCNTL:
00290 rc = fcntl(fileno(mStream), F_SETLKW, &fl);
00291
00292
if (rc < 0)
00293 {
00294 kdDebug(5006) <<
"Cannot lock folder `" << location() <<
"': "
00295 << strerror(errno) <<
" (" << errno <<
")" << endl;
00296
return errno;
00297 }
00298
00299
if (mIndexStream)
00300 {
00301 rc = fcntl(fileno(mIndexStream), F_SETLK, &fl);
00302
00303
if (rc < 0)
00304 {
00305 kdDebug(5006) <<
"Cannot lock index of folder `" << location() <<
"': "
00306 << strerror(errno) <<
" (" << errno <<
")" << endl;
00307 rc = errno;
00308 fl.l_type = F_UNLCK;
00309 rc = fcntl(fileno(mIndexStream), F_SETLK, &fl);
00310
return rc;
00311 }
00312 }
00313
break;
00314
00315
case procmail_lockfile:
00316 cmd_str =
"lockfile -l20 -r5 ";
00317
if (!mProcmailLockFileName.isEmpty())
00318 cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName));
00319
else
00320 cmd_str += QFile::encodeName(KProcess::quote(location() +
".lock"));
00321
00322 rc = system( cmd_str.data() );
00323
if( rc != 0 )
00324 {
00325 kdDebug(5006) <<
"Cannot lock folder `" << location() <<
"': "
00326 << strerror(rc) <<
" (" << rc <<
")" << endl;
00327
return rc;
00328 }
00329
if( mIndexStream )
00330 {
00331 cmd_str =
"lockfile -l20 -r5 " + QFile::encodeName(KProcess::quote(indexLocation() +
".lock"));
00332 rc = system( cmd_str.data() );
00333
if( rc != 0 )
00334 {
00335 kdDebug(5006) <<
"Cannot lock index of folder `" << location() <<
"': "
00336 << strerror(rc) <<
" (" << rc <<
")" << endl;
00337
return rc;
00338 }
00339 }
00340
break;
00341
00342
case mutt_dotlock:
00343 cmd_str =
"mutt_dotlock " + QFile::encodeName(KProcess::quote(location()));
00344 rc = system( cmd_str.data() );
00345
if( rc != 0 )
00346 {
00347 kdDebug(5006) <<
"Cannot lock folder `" << location() <<
"': "
00348 << strerror(rc) <<
" (" << rc <<
")" << endl;
00349
return rc;
00350 }
00351
if( mIndexStream )
00352 {
00353 cmd_str =
"mutt_dotlock " + QFile::encodeName(KProcess::quote(indexLocation()));
00354 rc = system( cmd_str.data() );
00355
if( rc != 0 )
00356 {
00357 kdDebug(5006) <<
"Cannot lock index of folder `" << location() <<
"': "
00358 << strerror(rc) <<
" (" << rc <<
")" << endl;
00359
return rc;
00360 }
00361 }
00362
break;
00363
00364
case mutt_dotlock_privileged:
00365 cmd_str =
"mutt_dotlock -p " + QFile::encodeName(KProcess::quote(location()));
00366 rc = system( cmd_str.data() );
00367
if( rc != 0 )
00368 {
00369 kdDebug(5006) <<
"Cannot lock folder `" << location() <<
"': "
00370 << strerror(rc) <<
" (" << rc <<
")" << endl;
00371
return rc;
00372 }
00373
if( mIndexStream )
00374 {
00375 cmd_str =
"mutt_dotlock -p " + QFile::encodeName(KProcess::quote(indexLocation()));
00376 rc = system( cmd_str.data() );
00377
if( rc != 0 )
00378 {
00379 kdDebug(5006) <<
"Cannot lock index of folder `" << location() <<
"': "
00380 << strerror(rc) <<
" (" << rc <<
")" << endl;
00381
return rc;
00382 }
00383 }
00384
break;
00385
00386
case lock_none:
00387
default:
00388
break;
00389 }
00390
00391
00392 mFilesLocked =
true;
00393
return 0;
00394 }
00395
00396
00397 FolderJob*
00398 KMFolderMbox::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
00399
KMFolder *folder, QString,
const AttachmentStrategy* )
const
00400
{
00401 MboxJob *job =
new MboxJob( msg, jt, folder );
00402 job->setParent(
this );
00403
return job;
00404 }
00405
00406
00407 FolderJob*
00408 KMFolderMbox::doCreateJob( QPtrList<KMMessage>& msgList,
const QString& sets,
00409 FolderJob::JobType jt,
KMFolder *folder )
const
00410
{
00411 MboxJob *job =
new MboxJob( msgList, sets, jt, folder );
00412 job->setParent(
this );
00413
return job;
00414 }
00415
00416
00417
int KMFolderMbox::unlock()
00418 {
00419
int rc;
00420
struct flock fl;
00421 fl.l_type=F_UNLCK;
00422 fl.l_whence=0;
00423 fl.l_start=0;
00424 fl.l_len=0;
00425 QCString cmd_str;
00426
00427 assert(mStream != 0);
00428 mFilesLocked =
false;
00429
00430
switch( mLockType )
00431 {
00432
case FCNTL:
00433
if (mIndexStream) fcntl(fileno(mIndexStream), F_SETLK, &fl);
00434 fcntl(fileno(mStream), F_SETLK, &fl);
00435 rc = errno;
00436
break;
00437
00438
case procmail_lockfile:
00439 cmd_str =
"rm -f ";
00440
if (!mProcmailLockFileName.isEmpty())
00441 cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName));
00442
else
00443 cmd_str += QFile::encodeName(KProcess::quote(location() +
".lock"));
00444
00445 rc = system( cmd_str.data() );
00446
if( mIndexStream )
00447 {
00448 cmd_str =
"rm -f " + QFile::encodeName(KProcess::quote(indexLocation() +
".lock"));
00449 rc = system( cmd_str.data() );
00450 }
00451
break;
00452
00453
case mutt_dotlock:
00454 cmd_str =
"mutt_dotlock -u " + QFile::encodeName(KProcess::quote(location()));
00455 rc = system( cmd_str.data() );
00456
if( mIndexStream )
00457 {
00458 cmd_str =
"mutt_dotlock -u " + QFile::encodeName(KProcess::quote(indexLocation()));
00459 rc = system( cmd_str.data() );
00460 }
00461
break;
00462
00463
case mutt_dotlock_privileged:
00464 cmd_str =
"mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(location()));
00465 rc = system( cmd_str.data() );
00466
if( mIndexStream )
00467 {
00468 cmd_str =
"mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(indexLocation()));
00469 rc = system( cmd_str.data() );
00470 }
00471
break;
00472
00473
case lock_none:
00474
default:
00475 rc = 0;
00476
break;
00477 }
00478
00479
return rc;
00480 }
00481
00482
00483
00484 KMFolderIndex::IndexStatus KMFolderMbox::indexStatus()
00485 {
00486 QFileInfo contInfo(location());
00487 QFileInfo indInfo(indexLocation());
00488
00489
if (!contInfo.exists())
return KMFolderIndex::IndexOk;
00490
if (!indInfo.exists())
return KMFolderIndex::IndexMissing;
00491
00492
00493
00494
00495
return ( contInfo.lastModified() > indInfo.lastModified().addSecs(5) )
00496 ? KMFolderIndex::IndexTooOld
00497 : KMFolderIndex::IndexOk;
00498 }
00499
00500
00501
00502
int KMFolderMbox::createIndexFromContents()
00503 {
00504
char line[MAX_LINE];
00505
char status[8], xstatus[8];
00506 QCString subjStr, dateStr, fromStr, toStr, xmarkStr, *lastStr=0;
00507 QCString replyToIdStr, replyToAuxIdStr, referencesStr, msgIdStr;
00508
bool atEof =
false;
00509
bool inHeader =
true;
00510 KMMsgInfo* mi;
00511 QString msgStr;
00512 QRegExp regexp(MSG_SEPERATOR_REGEX);
00513
int i, num, numStatus;
00514
short needStatus;
00515
00516 assert(mStream != 0);
00517 rewind(mStream);
00518
00519 mMsgList.clear();
00520
00521 num = -1;
00522 numStatus= 11;
00523 off_t offs = 0;
00524 size_t size = 0;
00525 dateStr =
"";
00526 fromStr =
"";
00527 toStr =
"";
00528 subjStr =
"";
00529 *status =
'\0';
00530 *xstatus =
'\0';
00531 xmarkStr =
"";
00532 replyToIdStr =
"";
00533 replyToAuxIdStr =
"";
00534 referencesStr =
"";
00535 msgIdStr =
"";
00536 needStatus = 3;
00537
00538
00539
while (!atEof)
00540 {
00541 off_t pos = ftell(mStream);
00542
if (!fgets(line, MAX_LINE, mStream)) atEof =
true;
00543
00544
if (atEof ||
00545 (strncmp(line,MSG_SEPERATOR_START, msgSepLen)==0 &&
00546 regexp.search(line) >= 0))
00547 {
00548 size = pos - offs;
00549 pos = ftell(mStream);
00550
00551
if (num >= 0)
00552 {
00553
if (numStatus <= 0)
00554 {
00555 msgStr = i18n(
"Creating index file: one message done",
"Creating index file: %n messages done", num);
00556 emit statusMsg(msgStr);
00557 numStatus = 10;
00558 }
00559
00560
if (size > 0)
00561 {
00562 msgIdStr = msgIdStr.stripWhiteSpace();
00563
if( !msgIdStr.isEmpty() ) {
00564
int rightAngle;
00565 rightAngle = msgIdStr.find(
'>' );
00566
if( rightAngle != -1 )
00567 msgIdStr.truncate( rightAngle + 1 );
00568 }
00569
00570 replyToIdStr = replyToIdStr.stripWhiteSpace();
00571
if( !replyToIdStr.isEmpty() ) {
00572
int rightAngle;
00573 rightAngle = replyToIdStr.find(
'>' );
00574
if( rightAngle != -1 )
00575 replyToIdStr.truncate( rightAngle + 1 );
00576 }
00577
00578 referencesStr = referencesStr.stripWhiteSpace();
00579
if( !referencesStr.isEmpty() ) {
00580
int leftAngle, rightAngle;
00581 leftAngle = referencesStr.findRev(
'<' );
00582
if( ( leftAngle != -1 )
00583 && ( replyToIdStr.isEmpty() || ( replyToIdStr[0] !=
'<' ) ) ) {
00584
00585 replyToIdStr = referencesStr.mid( leftAngle );
00586 }
00587
00588
00589 leftAngle = referencesStr.findRev(
'<', leftAngle - 1 );
00590
if( leftAngle != -1 )
00591 referencesStr = referencesStr.mid( leftAngle );
00592 rightAngle = referencesStr.findRev(
'>' );
00593
if( rightAngle != -1 )
00594 referencesStr.truncate( rightAngle + 1 );
00595
00596
00597
00598
00599
00600 replyToAuxIdStr = referencesStr;
00601 rightAngle = referencesStr.find(
'>' );
00602
if( rightAngle != -1 )
00603 replyToAuxIdStr.truncate( rightAngle + 1 );
00604 }
00605
00606 mi =
new KMMsgInfo(
this);
00607 mi->init( subjStr.stripWhiteSpace(),
00608 fromStr.stripWhiteSpace(),
00609 toStr.stripWhiteSpace(),
00610 0, KMMsgStatusNew,
00611 xmarkStr.stripWhiteSpace(),
00612 replyToIdStr, replyToAuxIdStr, msgIdStr,
00613 KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown,
00614 KMMsgMDNStateUnknown, offs, size );
00615 mi->setStatus(status, xstatus);
00616 mi->setDate( dateStr.stripWhiteSpace() );
00617 mi->setDirty(
false);
00618 mMsgList.append(mi);
00619
00620 *status =
'\0';
00621 *xstatus =
'\0';
00622 needStatus = 3;
00623 xmarkStr =
"";
00624 replyToIdStr =
"";
00625 replyToAuxIdStr =
"";
00626 referencesStr =
"";
00627 msgIdStr =
"";
00628 dateStr =
"";
00629 fromStr =
"";
00630 subjStr =
"";
00631 }
00632
else num--,numStatus++;
00633 }
00634
00635 offs = ftell(mStream);
00636 num++;
00637 numStatus--;
00638 inHeader =
true;
00639
continue;
00640 }
00641
00642
if (inHeader && (line[0]==
'\t' || line[0]==
' '))
00643 {
00644 i = 0;
00645
while (line [i]==
'\t' || line [i]==
' ') i++;
00646
if (line [i] <
' ' && line [i]>0) inHeader =
false;
00647
else if (lastStr) *lastStr += line + i;
00648 }
00649
else lastStr = 0;
00650
00651
if (inHeader && (line [0]==
'\n' || line [0]==
'\r'))
00652 inHeader =
false;
00653
if (!inHeader)
continue;
00654
00655
00656
00657
00658
if ((needStatus & 1) && strncasecmp(line,
"Status:", 7) == 0)
00659 {
00660
for(i=0; i<4 && line[i+8] >
' '; i++)
00661 status[i] = line[i+8];
00662 status[i] =
'\0';
00663 needStatus &= ~1;
00664 }
00665
else if ((needStatus & 2) && strncasecmp(line,
"X-Status:", 9)==0)
00666 {
00667
for(i=0; i<4 && line[i+10] >
' '; i++)
00668 xstatus[i] = line[i+10];
00669 xstatus[i] =
'\0';
00670 needStatus &= ~2;
00671 }
00672
else if (strncasecmp(line,
"X-KMail-Mark:",13)==0)
00673 xmarkStr = QCString(line+13);
00674
else if (strncasecmp(line,
"In-Reply-To:",12)==0) {
00675 replyToIdStr = QCString(line+12);
00676 lastStr = &replyToIdStr;
00677 }
00678
else if (strncasecmp(line,
"References:",11)==0) {
00679 referencesStr = QCString(line+11);
00680 lastStr = &referencesStr;
00681 }
00682
else if (strncasecmp(line,
"Message-Id:",11)==0) {
00683 msgIdStr = QCString(line+11);
00684 lastStr = &msgIdStr;
00685 }
00686
else if (strncasecmp(line,
"Date:",5)==0)
00687 {
00688 dateStr = QCString(line+5);
00689 lastStr = &dateStr;
00690 }
00691
else if (strncasecmp(line,
"From:", 5)==0)
00692 {
00693 fromStr = QCString(line+5);
00694 lastStr = &fromStr;
00695 }
00696
else if (strncasecmp(line,
"To:", 3)==0)
00697 {
00698 toStr = QCString(line+3);
00699 lastStr = &toStr;
00700 }
00701
else if (strncasecmp(line,
"Subject:",8)==0)
00702 {
00703 subjStr = QCString(line+8);
00704 lastStr = &subjStr;
00705 }
00706 }
00707
00708
if (mAutoCreateIndex)
00709 {
00710 emit statusMsg(i18n(
"Writing index file"));
00711 writeIndex();
00712 }
00713
else mHeaderOffset = 0;
00714
00715 correctUnreadMsgsCount();
00716
00717
if (kmkernel->outboxFolder() ==
this && count() > 0)
00718 KMessageBox::queuedMessageBox(0, KMessageBox::Information,
00719 i18n(
"Your outbox contains messages which were "
00720
"most likely not created by KMail.\nPlease remove them from there, if you "
00721
"don't want KMail to send them."));
00722
00723
if ( parent() )
00724 parent()->manager()->invalidateFolder(kmkernel->msgDict(),
this);
00725
return 0;
00726 }
00727
00728
00729
00730 KMMessage* KMFolderMbox::readMsg(
int idx)
00731 {
00732 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00733
00734 assert(mi!=0 && !mi->isMessage());
00735 assert(mStream != 0);
00736
00737 KMMessage* msg =
new KMMessage(*mi);
00738 msg->fromDwString( getDwString( idx ) );
00739 mMsgList.set(idx,&msg->toMsgBase());
00740
00741
return msg;
00742 }
00743
00744
00745
#define STRDIM(x) (sizeof(x)/sizeof(*x)-1)
00746
00747
static size_t unescapeFrom(
char* str, size_t strLen ) {
00748
if ( !str )
00749
return 0;
00750
if ( strLen <= STRDIM(
">From ") )
00751
return strLen;
00752
00753
00754
00755
00756
00757
const char * s = str;
00758
char * d = str;
00759
const char *
const e = str + strLen - STRDIM(
">From ");
00760
00761
while ( s < e ) {
00762
if ( *s ==
'\n' && *(s+1) ==
'>' ) {
00763 *d++ = *s++;
00764 *d++ = *s++;
00765
while ( s < e && *s == '>' )
00766 *d++ = *s++;
00767
if ( qstrncmp( s,
"From ", STRDIM(
"From ") ) == 0 )
00768 --d;
00769 }
00770 *d++ = *s++;
00771 }
00772
00773
while ( s < str + strLen )
00774 *d++ = *s++;
00775
if ( d < s )
00776 *d = 0;
00777
00778
return d - str;
00779 }
00780
00781
00782 QCString KMFolderMbox::escapeFrom(
const QCString & str ) {
00783
const unsigned int strLen = str.length();
00784
if ( strLen <= STRDIM(
"From ") )
00785
return str;
00786
00787 QCString result(
int( strLen + 5 ) / 6 * 7 + 1 );
00788
00789
const char * s = str.data();
00790
const char *
const e = s + strLen - STRDIM(
"From ");
00791
char * d = result.data();
00792
00793
bool onlyAnglesAfterLF =
false;
00794
while ( s < e ) {
00795
switch ( *s ) {
00796
case '\n':
00797 onlyAnglesAfterLF =
true;
00798
break;
00799
case '>':
00800
break;
00801
case 'F':
00802
if ( onlyAnglesAfterLF && qstrncmp( s+1,
"rom ", STRDIM(
"rom ") ) == 0 )
00803 *d++ =
'>';
00804
00805
default:
00806 onlyAnglesAfterLF =
false;
00807
break;
00808 }
00809 *d++ = *s++;
00810 }
00811
while ( s < str.data() + strLen )
00812 *d++ = *s++;
00813
00814 result.truncate( d - result.data() );
00815
return result;
00816 }
00817
00818
#undef STRDIM
00819
00820
00821 QCString& KMFolderMbox::getMsgString(
int idx, QCString &mDest)
00822 {
00823
unsigned long msgSize;
00824 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00825
00826 assert(mi!=0);
00827 assert(mStream != 0);
00828
00829 msgSize = mi->msgSize();
00830 mDest.resize(msgSize+2);
00831
00832 fseek(mStream, mi->folderOffset(), SEEK_SET);
00833 fread(mDest.data(), msgSize, 1, mStream);
00834 mDest[msgSize] =
'\0';
00835
00836 size_t newMsgSize = unescapeFrom( mDest.data(), msgSize );
00837 newMsgSize = crlf2lf( mDest.data(), newMsgSize );
00838
00839
return mDest;
00840 }
00841
00842
00843
00844 DwString KMFolderMbox::getDwString(
int idx)
00845 {
00846 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00847
00848 assert(mi!=0);
00849 assert(mStream != 0);
00850
00851 size_t msgSize = mi->msgSize();
00852
char* msgText =
new char[ msgSize + 1 ];
00853
00854 fseek(mStream, mi->folderOffset(), SEEK_SET);
00855 fread(msgText, msgSize, 1, mStream);
00856 msgText[msgSize] =
'\0';
00857
00858 size_t newMsgSize = unescapeFrom( msgText, msgSize );
00859 newMsgSize = crlf2lf( msgText, newMsgSize );
00860
00861 DwString msgStr;
00862
00863 msgStr.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize );
00864
return msgStr;
00865 }
00866
00867
00868
00869
int KMFolderMbox::addMsg(KMMessage* aMsg,
int* aIndex_ret)
00870 {
00871
if (!canAddMsgNow(aMsg, aIndex_ret))
return 0;
00872
bool opened =
false;
00873 QCString msgText;
00874
char endStr[3];
00875
int idx = -1, rc;
00876
KMFolder* msgParent;
00877
bool editing =
false;
00878
int growth = 0;
00879
00880
00881
00882
00883
00884
00885
00886
00887
if (!mStream)
00888 {
00889 opened =
true;
00890 rc = open();
00891 kdDebug(5006) <<
"addMsg-open: " << rc << endl;
00892
if (rc)
return rc;
00893 }
00894
00895
00896 msgParent = aMsg->parent();
00897
if (msgParent)
00898 {
00899
if (msgParent==
this)
00900 {
00901
if (kmkernel->folderIsDraftOrOutbox(
this))
00902
00903 {
00904 kdDebug(5006) <<
"Editing message in outbox or drafts" << endl;
00905 editing =
true;
00906 }
00907
else
00908
return 0;
00909 }
00910
00911 idx = msgParent->
find(aMsg);
00912 msgParent->
getMsg( idx );
00913 }
00914
00915
if (folderType() != KMFolderTypeImap)
00916 {
00917
00918
00919
00920
00921
00922
00923
00924
00925 aMsg->setStatusFields();
00926
00927
00928
00929
00930
00931
00932
00933
00934
if (aMsg->headerField(
"Content-Type").isEmpty())
00935 aMsg->removeHeaderField(
"Content-Type");
00936 }
00937 msgText = escapeFrom( aMsg->asString() );
00938 size_t len = msgText.length();
00939
00940 assert(mStream != 0);
00941 clearerr(mStream);
00942
if (len <= 0)
00943 {
00944 kdDebug(5006) <<
"Message added to folder `" << name() <<
"' contains no data. Ignoring it." << endl;
00945
if (opened) close();
00946
return 0;
00947 }
00948
00949
00950
00951 fseek(mStream, 0, SEEK_END);
00952 off_t revert = ftell(mStream);
00953
if (ftell(mStream) >= 2) {
00954
00955 fseek(mStream, -2, SEEK_END);
00956 fread(endStr, 1, 2, mStream);
00957
if (ftell(mStream) > 0 && endStr[0]!=
'\n') {
00958 ++growth;
00959
if (endStr[1]!=
'\n') {
00960
00961 fwrite(
"\n\n", 1, 2, mStream);
00962 ++growth;
00963 }
00964
else fwrite(
"\n", 1, 1, mStream);
00965 }
00966 }
00967 fseek(mStream,0,SEEK_END);
00968
int error = ferror(mStream);
00969
if (error)
00970 {
00971
if (opened) close();
00972
return error;
00973 }
00974
00975 QCString address( aMsg->fromEmail() );
00976
if ( address.isEmpty() )
00977 address =
"unknown@unknown.invalid";
00978 fprintf(mStream,
"From %s %s\n", address.data(),
00979 (
const char *)aMsg->dateShortStr());
00980 off_t offs = ftell(mStream);
00981 fwrite(msgText, len, 1, mStream);
00982
if (msgText[(
int)len-1]!=
'\n') fwrite(
"\n\n", 1, 2, mStream);
00983 fflush(mStream);
00984 size_t size = ftell(mStream) - offs;
00985
00986 error = ferror(mStream);
00987
if (error) {
00988 kdDebug(5006) <<
"Error: Could not add message to folder: " << strerror(errno) << endl;
00989
if (ftell(mStream) > revert) {
00990 kdDebug(5006) <<
"Undoing changes" << endl;
00991 truncate( QFile::encodeName(location()), revert );
00992 }
00993 kmkernel->emergencyExit( i18n(
"Could not add message to folder: ") + QString::fromLocal8Bit(strerror(errno)));
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
return error;
01007 }
01008
01009
if (msgParent) {
01010
if (idx >= 0) msgParent->
take(idx);
01011 }
01012
01013
01014
if (aMsg->isUnread() || aMsg->isNew() ||
01015 (
this == kmkernel->outboxFolder())) {
01016
if (mUnreadMsgs == -1) mUnreadMsgs = 1;
01017
else ++mUnreadMsgs;
01018 emit numUnreadMsgsChanged(
this );
01019 }
01020 ++mTotalMsgs;
01021
01022
01023 aMsg->setParent(
this);
01024 aMsg->setFolderOffset(offs);
01025 aMsg->setMsgSize(size);
01026 idx = mMsgList.append(&aMsg->toMsgBase());
01027
if (aMsg->getMsgSerNum() <= 0)
01028 aMsg->setMsgSerNum();
01029
01030
01031
if ((idx > 0) && (growth > 0)) {
01032
01033
if ((ulong)revert == mMsgList[idx - 1]->folderOffset() + mMsgList[idx - 1]->msgSize() )
01034 mMsgList[idx - 1]->setMsgSize( mMsgList[idx - 1]->msgSize() + growth );
01035 }
01036
01037
01038
if (mAutoCreateIndex)
01039 {
01040 assert(mIndexStream != 0);
01041 clearerr(mIndexStream);
01042 fseek(mIndexStream, 0, SEEK_END);
01043 revert = ftell(mIndexStream);
01044
01045 KMMsgBase * mb = &aMsg->toMsgBase();
01046
int len;
01047
const uchar *buffer = mb->asIndexString(len);
01048 fwrite(&len,
sizeof(len), 1, mIndexStream);
01049 mb->setIndexOffset( ftell(mIndexStream) );
01050 mb->setIndexLength( len );
01051
if(fwrite(buffer, len, 1, mIndexStream) != 1)
01052 kdDebug(5006) <<
"Whoa! " << __FILE__ <<
":" << __LINE__ << endl;
01053
01054 fflush(mIndexStream);
01055 error = ferror(mIndexStream);
01056
01057 error |= appendtoMsgDict(idx);
01058
01059
if (error) {
01060 kdWarning(5006) <<
"Error: Could not add message to folder (No space left on device?)" << endl;
01061
if (ftell(mIndexStream) > revert) {
01062 kdWarning(5006) <<
"Undoing changes" << endl;
01063 truncate( QFile::encodeName(indexLocation()), revert );
01064 }
01065
if ( errno )
01066 kmkernel->emergencyExit( i18n(
"Could not add message to folder:") + QString::fromLocal8Bit(strerror(errno)));
01067
else
01068 kmkernel->emergencyExit( i18n(
"Could not add message to folder (No space left on device?)") );
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
return error;
01081 }
01082 }
01083
01084
01085
if (aIndex_ret) *aIndex_ret = idx;
01086 emitMsgAddedSignals(idx);
01087
if (opened) close();
01088
01089
01090
01091
01092
return 0;
01093 }
01094
01095
01096
01097
int KMFolderMbox::compact()
01098 {
01099 QString tempName;
01100 QString msgStr;
01101
int rc = 0;
01102
int openCount = mOpenCount;
01103
01104
if (!needsCompact)
01105
return 0;
01106
01107
if (!mCompactable) {
01108 kdDebug(5006) << location() <<
" compaction skipped." << endl;
01109
return 0;
01110 }
01111 kdDebug(5006) <<
"Compacting " << idString() << endl;
01112
01113
if (KMFolderIndex::IndexOk != indexStatus()) {
01114 kdDebug(5006) <<
"Critical error: " << location() <<
01115
" has been modified by an external application while KMail was running." << endl;
01116
01117 }
01118
01119 tempName =
path() +
"/." + name() +
".compacted";
01120 mode_t old_umask = umask(077);
01121 FILE *tmpfile = fopen(QFile::encodeName(tempName),
"w");
01122 umask(old_umask);
01123
if (!tmpfile)
01124
return errno;
01125 open();
01126
01127 KMMsgInfo* mi;
01128 size_t msize;
01129 off_t folder_offset;
01130 off_t offs=0;
01131
int msgs=0;
01132 QCString mtext;
01133
for(
unsigned int idx = 0; idx < mMsgList.count(); idx++) {
01134
if(!(msgs++ % 10)) {
01135 msgStr = i18n(
"Compacting folder: one message done",
01136
"Compacting folder: %n messages done", msgs);
01137
if (!kmkernel->shuttingDown())
01138 emit statusMsg(msgStr);
01139 }
01140 mi = (KMMsgInfo*)mMsgList.at(idx);
01141 msize = mi->msgSize();
01142
if (mtext.size() < msize + 2)
01143 mtext.resize(msize+2);
01144 folder_offset = mi->folderOffset();
01145
01146
01147
for(off_t i = folder_offset-25;
true; i -= 20) {
01148 off_t chunk_offset = i <= 0 ? 0 : i;
01149
if(fseek(mStream, chunk_offset, SEEK_SET) == -1) {
01150 rc = errno;
01151
break;
01152 }
01153
if (mtext.size() < 20)
01154 mtext.resize(20);
01155 fread(mtext.data(), 20, 1, mStream);
01156
if(i <= 0) {
01157
if ( mtext.contains(
"from ",
false ) ) {
01158
if (mtext.size() < (size_t)folder_offset)
01159 mtext.resize(folder_offset);
01160
if(fseek(mStream, chunk_offset, SEEK_SET) == -1 ||
01161 !fread(mtext.data(), folder_offset, 1, mStream) ||
01162 !fwrite(mtext.data(), folder_offset, 1, tmpfile)) {
01163 rc = errno;
01164
break;
01165 }
01166 offs += folder_offset;
01167 }
else {
01168 rc = 666;
01169 }
01170
break;
01171 }
else {
01172
int last_crlf = -1;
01173
for(
int i2 = 0; i2 < 20; i2++) {
01174
if(*(mtext.data()+i2) ==
'\n')
01175 last_crlf = i2;
01176 }
01177
if(last_crlf != -1) {
01178
int size = folder_offset - (i + last_crlf+1);
01179
if ((
int)mtext.size() < size)
01180 mtext.resize(size);
01181
if(fseek(mStream, i + last_crlf+1, SEEK_SET) == -1 ||
01182 !fread(mtext.data(), size, 1, mStream) ||
01183 !fwrite(mtext.data(), size, 1, tmpfile)) {
01184 rc = errno;
01185
break;
01186 }
01187 offs += size;
01188
break;
01189 }
01190 }
01191 }
01192
if (rc)
01193
break;
01194
01195
01196
if(fseek(mStream, folder_offset, SEEK_SET) == -1 ||
01197 !fread(mtext.data(), msize, 1, mStream) || !fwrite(mtext.data(), msize, 1, tmpfile)) {
01198 rc = errno;
01199
break;
01200 }
01201 mi->setFolderOffset(offs);
01202 offs += msize;
01203 }
01204
if (!rc)
01205 rc = fflush(tmpfile);
01206
if (!rc)
01207 rc = fsync(fileno(tmpfile));
01208 rc |= fclose(tmpfile);
01209
if (!rc) {
01210
bool autoCreate = mAutoCreateIndex;
01211 QFileInfo inf(location());
01212 QString box;
01213
if (inf.isSymLink())
01214 box = inf.readLink();
01215
if (!box)
01216 box = location();
01217 ::rename(QFile::encodeName(tempName), QFile::encodeName(box));
01218 writeIndex();
01219 writeConfig();
01220 mAutoCreateIndex =
false;
01221 close(
true);
01222 mAutoCreateIndex = autoCreate;
01223 needsCompact =
false;
01224 }
01225
else
01226 {
01227 close();
01228 kdDebug(5006) <<
"Error occurred while compacting" << endl;
01229 kdDebug(5006) << location() << endl;
01230 kdDebug(5006) <<
"Compaction aborted." << endl;
01231 }
01232
01233
if (openCount > 0)
01234 {
01235 open();
01236 mOpenCount = openCount;
01237 }
01238 emit changed();
01239
return 0;
01240
01241 }
01242
01243
01244
01245
void KMFolderMbox::setLockType( LockType ltype )
01246 {
01247 mLockType = ltype;
01248 }
01249
01250
01251
void KMFolderMbox::setProcmailLockFileName(
const QString &fname )
01252 {
01253 mProcmailLockFileName = fname;
01254 }
01255
01256
01257
int KMFolderMbox::removeContents()
01258 {
01259
int rc = 0;
01260 rc = unlink(QFile::encodeName(location()));
01261
return rc;
01262 }
01263
01264
01265
int KMFolderMbox::expungeContents()
01266 {
01267
int rc = 0;
01268
if (truncate(QFile::encodeName(location()), 0))
01269 rc = errno;
01270
return rc;
01271 }
01272
01273
01274
#include "kmfoldermbox.moc"