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