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