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