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