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