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