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