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