00001
00002
00003
00004
00005 #ifdef HAVE_CONFIG_H
00006 #include <config.h>
00007 #endif
00008
00009 #include <qdir.h>
00010 #include <qregexp.h>
00011
00012 #include <libkdepim/kfileio.h>
00013 #include "kmfoldermaildir.h"
00014 #include "kmfoldermgr.h"
00015 #include "kmfolder.h"
00016 #include "undostack.h"
00017 #include "maildirjob.h"
00018 #include "kcursorsaver.h"
00019 #include "jobscheduler.h"
00020 using KMail::MaildirJob;
00021 #include "compactionjob.h"
00022 #include "kmmsgdict.h"
00023 #include "util.h"
00024
00025 #include <kapplication.h>
00026 #include <kdebug.h>
00027 #include <klocale.h>
00028 #include <kstaticdeleter.h>
00029 #include <kmessagebox.h>
00030 #include <kdirsize.h>
00031
00032 #include <dirent.h>
00033 #include <errno.h>
00034 #include <stdlib.h>
00035 #include <sys/stat.h>
00036 #include <sys/types.h>
00037 #include <unistd.h>
00038 #include <assert.h>
00039 #include <limits.h>
00040 #include <ctype.h>
00041 #include <fcntl.h>
00042
00043 #ifndef MAX_LINE
00044 #define MAX_LINE 4096
00045 #endif
00046 #ifndef INIT_MSGS
00047 #define INIT_MSGS 8
00048 #endif
00049
00050
00051 QValueList<KMFolderMaildir::DirSizeJobQueueEntry> KMFolderMaildir::s_DirSizeJobQueue;
00052
00053
00054 KMFolderMaildir::KMFolderMaildir(KMFolder* folder, const char* name)
00055 : KMFolderIndex(folder, name), mCurrentlyCheckingFolderSize(false)
00056 {
00057
00058 }
00059
00060
00061
00062 KMFolderMaildir::~KMFolderMaildir()
00063 {
00064 if (mOpenCount>0) close("~foldermaildir", true);
00065 if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
00066 }
00067
00068
00069 int KMFolderMaildir::canAccess()
00070 {
00071
00072 assert(!folder()->name().isEmpty());
00073
00074 QString sBadFolderName;
00075 if (access(QFile::encodeName(location()), R_OK | W_OK | X_OK) != 0) {
00076 sBadFolderName = location();
00077 } else if (access(QFile::encodeName(location() + "/new"), R_OK | W_OK | X_OK) != 0) {
00078 sBadFolderName = location() + "/new";
00079 } else if (access(QFile::encodeName(location() + "/cur"), R_OK | W_OK | X_OK) != 0) {
00080 sBadFolderName = location() + "/cur";
00081 } else if (access(QFile::encodeName(location() + "/tmp"), R_OK | W_OK | X_OK) != 0) {
00082 sBadFolderName = location() + "/tmp";
00083 }
00084
00085 if ( !sBadFolderName.isEmpty() ) {
00086 int nRetVal = QFile::exists(sBadFolderName) ? EPERM : ENOENT;
00087 KCursorSaver idle(KBusyPtr::idle());
00088 if ( nRetVal == ENOENT )
00089 KMessageBox::sorry(0, i18n("Error opening %1; this folder is missing.")
00090 .arg(sBadFolderName));
00091 else
00092 KMessageBox::sorry(0, i18n("Error opening %1; either this is not a valid "
00093 "maildir folder, or you do not have sufficient access permissions.")
00094 .arg(sBadFolderName));
00095 return nRetVal;
00096 }
00097
00098 return 0;
00099 }
00100
00101
00102 int KMFolderMaildir::open(const char *)
00103 {
00104 int rc = 0;
00105
00106 mOpenCount++;
00107 kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
00108
00109 if (mOpenCount > 1) return 0;
00110
00111 assert(!folder()->name().isEmpty());
00112
00113 rc = canAccess();
00114 if ( rc != 0 ) {
00115 return rc;
00116 }
00117
00118 if (!folder()->path().isEmpty())
00119 {
00120 if (KMFolderIndex::IndexOk != indexStatus())
00121 {
00122 QString str;
00123 mIndexStream = 0;
00124 str = i18n("Folder `%1' changed; recreating index.")
00125 .arg(name());
00126 emit statusMsg(str);
00127 } else {
00128 mIndexStream = fopen(QFile::encodeName(indexLocation()), "r+");
00129 if ( mIndexStream ) {
00130 fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00131 updateIndexStreamPtr();
00132 }
00133 }
00134
00135 if (!mIndexStream)
00136 rc = createIndexFromContents();
00137 else
00138 readIndex();
00139 }
00140 else
00141 {
00142 mAutoCreateIndex = false;
00143 rc = createIndexFromContents();
00144 }
00145
00146 mChanged = false;
00147
00148
00149
00150 return rc;
00151 }
00152
00153
00154
00155 int KMFolderMaildir::createMaildirFolders( const QString & folderPath )
00156 {
00157
00158 QFileInfo dirinfo;
00159 dirinfo.setFile( folderPath + "/new" );
00160 if ( dirinfo.exists() ) return EEXIST;
00161 dirinfo.setFile( folderPath + "/cur" );
00162 if ( dirinfo.exists() ) return EEXIST;
00163 dirinfo.setFile( folderPath + "/tmp" );
00164 if ( dirinfo.exists() ) return EEXIST;
00165
00166
00167 if ( ::mkdir( QFile::encodeName( folderPath ), S_IRWXU ) > 0 ) {
00168 kdDebug(5006) << "Could not create folder " << folderPath << endl;
00169 return errno;
00170 }
00171 if ( ::mkdir( QFile::encodeName( folderPath + "/new" ), S_IRWXU ) > 0 ) {
00172 kdDebug(5006) << "Could not create folder " << folderPath << "/new" << endl;
00173 return errno;
00174 }
00175 if ( ::mkdir( QFile::encodeName( folderPath + "/cur" ), S_IRWXU ) > 0 ) {
00176 kdDebug(5006) << "Could not create folder " << folderPath << "/cur" << endl;
00177 return errno;
00178 }
00179 if ( ::mkdir( QFile::encodeName( folderPath + "/tmp" ), S_IRWXU ) > 0 ) {
00180 kdDebug(5006) << "Could not create folder " << folderPath << "/tmp" << endl;
00181 return errno;
00182 }
00183
00184 return 0;
00185 }
00186
00187
00188 int KMFolderMaildir::create()
00189 {
00190 int rc;
00191 int old_umask;
00192
00193 assert(!folder()->name().isEmpty());
00194 assert(mOpenCount == 0);
00195
00196 rc = createMaildirFolders( location() );
00197 if ( rc != 0 )
00198 return rc;
00199
00200
00201 if (!folder()->path().isEmpty())
00202 {
00203 old_umask = umask(077);
00204 mIndexStream = fopen(QFile::encodeName(indexLocation()), "w+");
00205 updateIndexStreamPtr(true);
00206 umask(old_umask);
00207
00208 if (!mIndexStream) return errno;
00209 fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
00210 }
00211 else
00212 {
00213 mAutoCreateIndex = false;
00214 }
00215
00216 mOpenCount++;
00217 mChanged = false;
00218
00219 rc = writeIndex();
00220 return rc;
00221 }
00222
00223
00224
00225 void KMFolderMaildir::reallyDoClose(const char* owner)
00226 {
00227 if (mAutoCreateIndex)
00228 {
00229 updateIndex();
00230 writeConfig();
00231 }
00232
00233 mMsgList.clear(true);
00234
00235 if (mIndexStream) {
00236 fclose(mIndexStream);
00237 updateIndexStreamPtr(true);
00238 }
00239
00240 mOpenCount = 0;
00241 mIndexStream = 0;
00242 mUnreadMsgs = -1;
00243
00244 mMsgList.reset(INIT_MSGS);
00245 }
00246
00247
00248 void KMFolderMaildir::sync()
00249 {
00250 if (mOpenCount > 0)
00251 if (!mIndexStream || fsync(fileno(mIndexStream))) {
00252 kmkernel->emergencyExit( i18n("Could not sync maildir folder.") );
00253 }
00254 }
00255
00256
00257 int KMFolderMaildir::expungeContents()
00258 {
00259
00260 QDir d(location() + "/new");
00261
00262 QStringList files(d.entryList());
00263 QStringList::ConstIterator it(files.begin());
00264 for ( ; it != files.end(); ++it)
00265 QFile::remove(d.filePath(*it));
00266
00267 d.setPath(location() + "/cur");
00268 files = d.entryList();
00269 for (it = files.begin(); it != files.end(); ++it)
00270 QFile::remove(d.filePath(*it));
00271
00272 return 0;
00273 }
00274
00275 int KMFolderMaildir::compact( unsigned int startIndex, int nbMessages, const QStringList& entryList, bool& done )
00276 {
00277 QString subdirNew(location() + "/new/");
00278 QString subdirCur(location() + "/cur/");
00279
00280 unsigned int stopIndex = nbMessages == -1 ? mMsgList.count() :
00281 QMIN( mMsgList.count(), startIndex + nbMessages );
00282
00283 for(unsigned int idx = startIndex; idx < stopIndex; ++idx) {
00284 KMMsgInfo* mi = (KMMsgInfo*)mMsgList.at(idx);
00285 if (!mi)
00286 continue;
00287
00288 QString filename(mi->fileName());
00289 if (filename.isEmpty())
00290 continue;
00291
00292
00293 if ( entryList.contains( filename ) )
00294 moveInternal(subdirNew + filename, subdirCur + filename, mi);
00295
00296
00297
00298 filename = constructValidFileName( filename, mi->status() );
00299
00300
00301 if (filename != mi->fileName())
00302 {
00303 moveInternal(subdirCur + mi->fileName(), subdirCur + filename, mi);
00304 mi->setFileName(filename);
00305 setDirty( true );
00306 }
00307
00308 #if 0
00309
00310 if (mi->isNew())
00311 {
00312 mi->setStatus(KMMsgStatusUnread);
00313 setDirty( true );
00314 }
00315 #endif
00316 }
00317 done = ( stopIndex == mMsgList.count() );
00318 return 0;
00319 }
00320
00321
00322 int KMFolderMaildir::compact( bool silent )
00323 {
00324 KMail::MaildirCompactionJob* job = new KMail::MaildirCompactionJob( folder(), true );
00325 int rc = job->executeNow( silent );
00326
00327 return rc;
00328 }
00329
00330
00331 FolderJob*
00332 KMFolderMaildir::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
00333 KMFolder *folder, QString, const AttachmentStrategy* ) const
00334 {
00335 MaildirJob *job = new MaildirJob( msg, jt, folder );
00336 job->setParentFolder( this );
00337 return job;
00338 }
00339
00340
00341 FolderJob*
00342 KMFolderMaildir::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
00343 FolderJob::JobType jt, KMFolder *folder ) const
00344 {
00345 MaildirJob *job = new MaildirJob( msgList, sets, jt, folder );
00346 job->setParentFolder( this );
00347 return job;
00348 }
00349
00350
00351 int KMFolderMaildir::addMsg(KMMessage* aMsg, int* index_return)
00352 {
00353 if (!canAddMsgNow(aMsg, index_return)) return 0;
00354 return addMsgInternal( aMsg, index_return );
00355 }
00356
00357
00358 int KMFolderMaildir::addMsgInternal( KMMessage* aMsg, int* index_return,
00359 bool stripUid )
00360 {
00361
00362
00363
00364
00365
00366
00367
00368
00369 long len;
00370 unsigned long size;
00371 KMFolder* msgParent;
00372 QCString msgText;
00373 int idx(-1);
00374 int rc;
00375
00376
00377 msgParent = aMsg->parent();
00378 if (msgParent)
00379 {
00380 if (msgParent==folder() && !kmkernel->folderIsDraftOrOutbox(folder()))
00381 return 0;
00382
00383 idx = msgParent->find(aMsg);
00384 msgParent->getMsg( idx );
00385 }
00386
00387 aMsg->setStatusFields();
00388 if (aMsg->headerField("Content-Type").isEmpty())
00389 aMsg->removeHeaderField("Content-Type");
00390
00391
00392 const QString uidHeader = aMsg->headerField( "X-UID" );
00393 if ( !uidHeader.isEmpty() && stripUid )
00394 aMsg->removeHeaderField( "X-UID" );
00395
00396 msgText = aMsg->asString();
00397 len = msgText.length();
00398
00399
00400
00401 if ( !uidHeader.isEmpty() && stripUid )
00402 aMsg->setHeaderField( "X-UID", uidHeader );
00403
00404 if (len <= 0)
00405 {
00406 kdDebug(5006) << "Message added to folder `" << name() << "' contains no data. Ignoring it." << endl;
00407 return 0;
00408 }
00409
00410
00411 QString filename = constructValidFileName( aMsg->fileName(), aMsg->status() );
00412
00413 QString tmp_file(location() + "/tmp/");
00414 tmp_file += filename;
00415
00416 if (!KPIM::kCStringToFile(msgText, tmp_file, false, false, false))
00417 kmkernel->emergencyExit( i18n("Message could not be added to the folder, possibly disk space is low.") );
00418
00419 QFile file(tmp_file);
00420 size = msgText.length();
00421
00422 KMFolderOpener openThis(folder(), "maildir");
00423 rc = openThis.openResult();
00424 if (rc)
00425 {
00426 kdDebug(5006) << "KMFolderMaildir::addMsg-open: " << rc << " of folder: " << label() << endl;
00427 return rc;
00428 }
00429
00430
00431 QString new_loc(location() + "/cur/");
00432 new_loc += filename;
00433 if (moveInternal(tmp_file, new_loc, filename, aMsg->status()).isNull())
00434 {
00435 file.remove();
00436 return -1;
00437 }
00438
00439 if (msgParent && idx >= 0)
00440 msgParent->take(idx);
00441
00442
00443 if ( stripUid ) aMsg->setUID( 0 );
00444
00445 if (filename != aMsg->fileName())
00446 aMsg->setFileName(filename);
00447
00448 if (aMsg->isUnread() || aMsg->isNew() || folder() == kmkernel->outboxFolder())
00449 {
00450 if (mUnreadMsgs == -1)
00451 mUnreadMsgs = 1;
00452 else
00453 ++mUnreadMsgs;
00454 if ( !mQuiet ) {
00455 kdDebug( 5006 ) << "FolderStorage::msgStatusChanged" << endl;
00456 emit numUnreadMsgsChanged( folder() );
00457 }else{
00458 if ( !mEmitChangedTimer->isActive() ) {
00459
00460 mEmitChangedTimer->start( 3000 );
00461 }
00462 mChanged = true;
00463 }
00464 }
00465 ++mTotalMsgs;
00466 mSize = -1;
00467
00468 if ( aMsg->attachmentState() == KMMsgAttachmentUnknown &&
00469 aMsg->readyToShow() )
00470 aMsg->updateAttachmentState();
00471
00472
00473 aMsg->setParent(folder());
00474 aMsg->setMsgSize(size);
00475 idx = mMsgList.append( &aMsg->toMsgBase(), mExportsSernums );
00476 if (aMsg->getMsgSerNum() <= 0)
00477 aMsg->setMsgSerNum();
00478 else
00479 replaceMsgSerNum( aMsg->getMsgSerNum(), &aMsg->toMsgBase(), idx );
00480
00481
00482 if (mAutoCreateIndex)
00483 {
00484 assert(mIndexStream != 0);
00485 clearerr(mIndexStream);
00486 fseek(mIndexStream, 0, SEEK_END);
00487 off_t revert = ftell(mIndexStream);
00488
00489 int len;
00490 KMMsgBase * mb = &aMsg->toMsgBase();
00491 const uchar *buffer = mb->asIndexString(len);
00492 fwrite(&len,sizeof(len), 1, mIndexStream);
00493 mb->setIndexOffset( ftell(mIndexStream) );
00494 mb->setIndexLength( len );
00495 if(fwrite(buffer, len, 1, mIndexStream) != 1)
00496 kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
00497
00498 fflush(mIndexStream);
00499 int error = ferror(mIndexStream);
00500
00501 if ( mExportsSernums )
00502 error |= appendToFolderIdsFile( idx );
00503
00504 if (error) {
00505 kdDebug(5006) << "Error: Could not add message to folder (No space left on device?)" << endl;
00506 if (ftell(mIndexStream) > revert) {
00507 kdDebug(5006) << "Undoing changes" << endl;
00508 truncate( QFile::encodeName(indexLocation()), revert );
00509 }
00510 kmkernel->emergencyExit(i18n("KMFolderMaildir::addMsg: abnormally terminating to prevent data loss."));
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522 return error;
00523 }
00524 }
00525
00526 if (index_return)
00527 *index_return = idx;
00528
00529 emitMsgAddedSignals(idx);
00530 needsCompact = true;
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540 return 0;
00541 }
00542
00543 KMMessage* KMFolderMaildir::readMsg(int idx)
00544 {
00545 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00546 KMMessage *msg = new KMMessage(*mi);
00547 msg->setMsgInfo( mi );
00548 mMsgList.set(idx,&msg->toMsgBase());
00549 msg->setComplete( true );
00550 msg->fromDwString(getDwString(idx));
00551 return msg;
00552 }
00553
00554 DwString KMFolderMaildir::getDwString(int idx)
00555 {
00556 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
00557 QString abs_file(location() + "/cur/");
00558 abs_file += mi->fileName();
00559 QFileInfo fi( abs_file );
00560
00561 if (fi.exists() && fi.isFile() && fi.isWritable() && fi.size() > 0)
00562 {
00563 FILE* stream = fopen(QFile::encodeName(abs_file), "r+");
00564 if (stream) {
00565 size_t msgSize = fi.size();
00566 char* msgText = new char[ msgSize + 1 ];
00567 fread(msgText, msgSize, 1, stream);
00568 fclose( stream );
00569 msgText[msgSize] = '\0';
00570 size_t newMsgSize = KMail::Util::crlf2lf( msgText, msgSize );
00571 DwString str;
00572
00573 str.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize );
00574 return str;
00575 }
00576 }
00577 kdDebug(5006) << "Could not open file r+ " << abs_file << endl;
00578 return DwString();
00579 }
00580
00581
00582 void KMFolderMaildir::readFileHeaderIntern(const QString& dir, const QString& file, KMMsgStatus status)
00583 {
00584
00585 char path_buffer[PATH_MAX];
00586 if(!::getcwd(path_buffer, PATH_MAX - 1))
00587 return;
00588
00589 ::chdir(QFile::encodeName(dir));
00590
00591
00592
00593 if (status == KMMsgStatusRead)
00594 {
00595 if (file.find(":2,") == -1)
00596 status = KMMsgStatusUnread;
00597 else if (file.right(5) == ":2,RS")
00598 status |= KMMsgStatusReplied;
00599 }
00600
00601
00602 QFile f(file);
00603 if ( f.open( IO_ReadOnly ) == false ) {
00604 kdWarning(5006) << "The file '" << QFile::encodeName(dir) << "/" << file
00605 << "' could not be opened for reading the message. "
00606 "Please check ownership and permissions."
00607 << endl;
00608 return;
00609 }
00610
00611 char line[MAX_LINE];
00612 bool atEof = false;
00613 bool inHeader = true;
00614 QCString *lastStr = 0;
00615
00616 QCString dateStr, fromStr, toStr, subjStr;
00617 QCString xmarkStr, replyToIdStr, msgIdStr, referencesStr;
00618 QCString statusStr, replyToAuxIdStr, uidStr;
00619 QCString contentTypeStr, charset;
00620
00621
00622 while (!atEof)
00623 {
00624
00625 if ( f.atEnd() || ( -1 == f.readLine(line, MAX_LINE) ) )
00626 atEof = true;
00627
00628
00629
00630 if (atEof || !inHeader)
00631 {
00632 msgIdStr = msgIdStr.stripWhiteSpace();
00633 if( !msgIdStr.isEmpty() ) {
00634 int rightAngle;
00635 rightAngle = msgIdStr.find( '>' );
00636 if( rightAngle != -1 )
00637 msgIdStr.truncate( rightAngle + 1 );
00638 }
00639
00640 replyToIdStr = replyToIdStr.stripWhiteSpace();
00641 if( !replyToIdStr.isEmpty() ) {
00642 int rightAngle;
00643 rightAngle = replyToIdStr.find( '>' );
00644 if( rightAngle != -1 )
00645 replyToIdStr.truncate( rightAngle + 1 );
00646 }
00647
00648 referencesStr = referencesStr.stripWhiteSpace();
00649 if( !referencesStr.isEmpty() ) {
00650 int leftAngle, rightAngle;
00651 leftAngle = referencesStr.findRev( '<' );
00652 if( ( leftAngle != -1 )
00653 && ( replyToIdStr.isEmpty() || ( replyToIdStr[0] != '<' ) ) ) {
00654
00655 replyToIdStr = referencesStr.mid( leftAngle );
00656 }
00657
00658
00659 leftAngle = referencesStr.findRev( '<', leftAngle - 1 );
00660 if( leftAngle != -1 )
00661 referencesStr = referencesStr.mid( leftAngle );
00662 rightAngle = referencesStr.findRev( '>' );
00663 if( rightAngle != -1 )
00664 referencesStr.truncate( rightAngle + 1 );
00665
00666
00667
00668
00669
00670 replyToAuxIdStr = referencesStr;
00671 rightAngle = referencesStr.find( '>' );
00672 if( rightAngle != -1 )
00673 replyToAuxIdStr.truncate( rightAngle + 1 );
00674 }
00675
00676 statusStr = statusStr.stripWhiteSpace();
00677 if (!statusStr.isEmpty())
00678 {
00679
00680 if (statusStr[0] == 'S')
00681 status |= KMMsgStatusSent;
00682 else if (statusStr[0] == 'F')
00683 status |= KMMsgStatusForwarded;
00684 else if (statusStr[0] == 'D')
00685 status |= KMMsgStatusDeleted;
00686 else if (statusStr[0] == 'Q')
00687 status |= KMMsgStatusQueued;
00688 else if (statusStr[0] == 'G')
00689 status |= KMMsgStatusFlag;
00690 }
00691
00692 contentTypeStr = contentTypeStr.stripWhiteSpace();
00693 charset = "";
00694 if ( !contentTypeStr.isEmpty() )
00695 {
00696 int cidx = contentTypeStr.find( "charset=" );
00697 if ( cidx != -1 ) {
00698 charset = contentTypeStr.mid( cidx + 8 );
00699 if ( !charset.isEmpty() && ( charset[0] == '"' ) ) {
00700 charset = charset.mid( 1 );
00701 }
00702 cidx = 0;
00703 while ( (unsigned int) cidx < charset.length() ) {
00704 if ( charset[cidx] == '"' || ( !isalnum(charset[cidx]) &&
00705 charset[cidx] != '-' && charset[cidx] != '_' ) )
00706 break;
00707 ++cidx;
00708 }
00709 charset.truncate( cidx );
00710
00711
00712 }
00713 }
00714
00715 KMMsgInfo *mi = new KMMsgInfo(folder());
00716 mi->init( subjStr.stripWhiteSpace(),
00717 fromStr.stripWhiteSpace(),
00718 toStr.stripWhiteSpace(),
00719 0, status,
00720 xmarkStr.stripWhiteSpace(),
00721 replyToIdStr, replyToAuxIdStr, msgIdStr,
00722 file.local8Bit(),
00723 KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown,
00724 KMMsgMDNStateUnknown, charset, f.size() );
00725
00726 dateStr = dateStr.stripWhiteSpace();
00727 if (!dateStr.isEmpty())
00728 mi->setDate(dateStr);
00729 if ( !uidStr.isEmpty() )
00730 mi->setUID( uidStr.toULong() );
00731 mi->setDirty(false);
00732 mMsgList.append( mi, mExportsSernums );
00733
00734
00735 if (status & KMMsgStatusNew)
00736 {
00737 QString newDir(location() + "/new/");
00738 QString curDir(location() + "/cur/");
00739 moveInternal(newDir + file, curDir + file, mi);
00740 }
00741
00742 break;
00743 }
00744
00745
00746 if (inHeader && line[0] == '\t' || line[0] == ' ')
00747 {
00748 int i = 0;
00749 while (line[i] == '\t' || line[i] == ' ')
00750 i++;
00751 if (line[i] < ' ' && line[i] > 0)
00752 inHeader = false;
00753 else
00754 if (lastStr)
00755 *lastStr += line + i;
00756 }
00757 else
00758 lastStr = 0;
00759
00760 if (inHeader && (line[0] == '\n' || line[0] == '\r'))
00761 inHeader = false;
00762 if (!inHeader)
00763 continue;
00764
00765 if (strncasecmp(line, "Date:", 5) == 0)
00766 {
00767 dateStr = QCString(line+5);
00768 lastStr = &dateStr;
00769 }
00770 else if (strncasecmp(line, "From:", 5) == 0)
00771 {
00772 fromStr = QCString(line+5);
00773 lastStr = &fromStr;
00774 }
00775 else if (strncasecmp(line, "To:", 3) == 0)
00776 {
00777 toStr = QCString(line+3);
00778 lastStr = &toStr;
00779 }
00780 else if (strncasecmp(line, "Subject:", 8) == 0)
00781 {
00782 subjStr = QCString(line+8);
00783 lastStr = &subjStr;
00784 }
00785 else if (strncasecmp(line, "References:", 11) == 0)
00786 {
00787 referencesStr = QCString(line+11);
00788 lastStr = &referencesStr;
00789 }
00790 else if (strncasecmp(line, "Message-Id:", 11) == 0)
00791 {
00792 msgIdStr = QCString(line+11);
00793 lastStr = &msgIdStr;
00794 }
00795 else if (strncasecmp(line, "X-KMail-Mark:", 13) == 0)
00796 {
00797 xmarkStr = QCString(line+13);
00798 }
00799 else if (strncasecmp(line, "X-Status:", 9) == 0)
00800 {
00801 statusStr = QCString(line+9);
00802 }
00803 else if (strncasecmp(line, "In-Reply-To:", 12) == 0)
00804 {
00805 replyToIdStr = QCString(line+12);
00806 lastStr = &replyToIdStr;
00807 }
00808 else if (strncasecmp(line, "X-UID:", 6) == 0)
00809 {
00810 uidStr = QCString(line+6);
00811 lastStr = &uidStr;
00812 }
00813 else if (strncasecmp(line, "Content-Type:", 13) == 0)
00814 {
00815 contentTypeStr = QCString(line+13);
00816 lastStr = &contentTypeStr;
00817 }
00818
00819 }
00820
00821 if (status & KMMsgStatusNew || status & KMMsgStatusUnread ||
00822 (folder() == kmkernel->outboxFolder()))
00823 {
00824 mUnreadMsgs++;
00825 if (mUnreadMsgs == 0) ++mUnreadMsgs;
00826 }
00827
00828 ::chdir(path_buffer);
00829 }
00830
00831 int KMFolderMaildir::createIndexFromContents()
00832 {
00833 mUnreadMsgs = 0;
00834
00835 mMsgList.clear(true);
00836 mMsgList.reset(INIT_MSGS);
00837
00838 mChanged = false;
00839
00840
00841
00842 QFileInfo dirinfo;
00843
00844 dirinfo.setFile(location() + "/new");
00845 if (!dirinfo.exists() || !dirinfo.isDir())
00846 {
00847 kdDebug(5006) << "Directory " << location() << "/new doesn't exist or is a file"<< endl;
00848 return 1;
00849 }
00850 QDir newDir(location() + "/new");
00851 newDir.setFilter(QDir::Files);
00852
00853 dirinfo.setFile(location() + "/cur");
00854 if (!dirinfo.exists() || !dirinfo.isDir())
00855 {
00856 kdDebug(5006) << "Directory " << location() << "/cur doesn't exist or is a file"<< endl;
00857 return 1;
00858 }
00859 QDir curDir(location() + "/cur");
00860 curDir.setFilter(QDir::Files);
00861
00862
00863 const QFileInfoList *list = curDir.entryInfoList();
00864 QFileInfoListIterator it(*list);
00865 QFileInfo *fi;
00866
00867 while ((fi = it.current()))
00868 {
00869 readFileHeaderIntern(curDir.path(), fi->fileName(), KMMsgStatusRead);
00870 ++it;
00871 }
00872
00873
00874 list = newDir.entryInfoList();
00875 it = *list;
00876
00877 while ((fi=it.current()))
00878 {
00879 readFileHeaderIntern(newDir.path(), fi->fileName(), KMMsgStatusNew);
00880 ++it;
00881 }
00882
00883 if ( autoCreateIndex() ) {
00884 emit statusMsg(i18n("Writing index file"));
00885 writeIndex();
00886 }
00887 else mHeaderOffset = 0;
00888
00889 correctUnreadMsgsCount();
00890
00891 if (kmkernel->outboxFolder() == folder() && count() > 0)
00892 KMessageBox::information(0, i18n("Your outbox contains messages which were "
00893 "most-likely not created by KMail;\nplease remove them from there if you "
00894 "do not want KMail to send them."));
00895
00896 needsCompact = true;
00897
00898 invalidateFolder();
00899 return 0;
00900 }
00901
00902 KMFolderIndex::IndexStatus KMFolderMaildir::indexStatus()
00903 {
00904 QFileInfo new_info(location() + "/new");
00905 QFileInfo cur_info(location() + "/cur");
00906 QFileInfo index_info(indexLocation());
00907
00908 if (!index_info.exists())
00909 return KMFolderIndex::IndexMissing;
00910
00911
00912
00913
00914 return ((new_info.lastModified() > index_info.lastModified().addSecs(5)) ||
00915 (cur_info.lastModified() > index_info.lastModified().addSecs(5)))
00916 ? KMFolderIndex::IndexTooOld
00917 : KMFolderIndex::IndexOk;
00918 }
00919
00920
00921 void KMFolderMaildir::removeMsg(int idx, bool)
00922 {
00923 KMMsgBase* msg = mMsgList[idx];
00924 if (!msg || !msg->fileName()) return;
00925
00926 removeFile(msg->fileName());
00927
00928 KMFolderIndex::removeMsg(idx);
00929 }
00930
00931
00932 KMMessage* KMFolderMaildir::take(int idx)
00933 {
00934
00935 KMMessage *msg = KMFolderIndex::take(idx);
00936
00937 if (!msg || !msg->fileName()) {
00938 return 0;
00939 }
00940
00941 if ( removeFile(msg->fileName()) ) {
00942 return msg;
00943 } else {
00944 return 0;
00945 }
00946 }
00947
00948
00949 bool KMFolderMaildir::removeFile( const QString & folderPath,
00950 const QString & filename )
00951 {
00952
00953
00954
00955
00956 QCString abs_file( QFile::encodeName( folderPath + "/cur/" + filename ) );
00957 if ( ::unlink( abs_file ) == 0 )
00958 return true;
00959
00960 if ( errno == ENOENT ) {
00961 abs_file = QFile::encodeName( folderPath + "/new/" + filename );
00962 if ( ::unlink( abs_file ) == 0 )
00963 return true;
00964 }
00965
00966 kdDebug(5006) << "Can't delete " << abs_file << " " << perror << endl;
00967 return false;
00968 }
00969
00970 bool KMFolderMaildir::removeFile( const QString & filename )
00971 {
00972 return removeFile( location(), filename );
00973 }
00974
00975 #include <sys/types.h>
00976 #include <dirent.h>
00977 static bool removeDirAndContentsRecursively( const QString & path )
00978 {
00979 bool success = true;
00980
00981 QDir d;
00982 d.setPath( path );
00983 d.setFilter( QDir::Files | QDir::Dirs | QDir::Hidden | QDir::NoSymLinks );
00984
00985 const QFileInfoList *list = d.entryInfoList();
00986 QFileInfoListIterator it( *list );
00987 QFileInfo *fi;
00988
00989 while ( (fi = it.current()) != 0 ) {
00990 if( fi->isDir() ) {
00991 if ( fi->fileName() != "." && fi->fileName() != ".." )
00992 success = success && removeDirAndContentsRecursively( fi->absFilePath() );
00993 } else {
00994 success = success && d.remove( fi->absFilePath() );
00995 }
00996 ++it;
00997 }
00998
00999 if ( success ) {
01000 success = success && d.rmdir( path );
01001 }
01002 return success;
01003 }
01004
01005
01006 int KMFolderMaildir::removeContents()
01007 {
01008
01009
01010 if ( !removeDirAndContentsRecursively( location() + "/new/" ) ) return 1;
01011 if ( !removeDirAndContentsRecursively( location() + "/cur/" ) ) return 1;
01012 if ( !removeDirAndContentsRecursively( location() + "/tmp/" ) ) return 1;
01013
01014
01015
01016 QDir dir(location());
01017 if ( dir.count() == 2 ) {
01018 if ( !removeDirAndContentsRecursively( location() ), 0 ) return 1;
01019 }
01020 return 0;
01021 }
01022
01023 static QRegExp *suffix_regex = 0;
01024 static KStaticDeleter<QRegExp> suffix_regex_sd;
01025
01026
01027
01028 QString KMFolderMaildir::constructValidFileName( const QString & filename,
01029 KMMsgStatus status )
01030 {
01031 QString aFileName( filename );
01032
01033 if (aFileName.isEmpty())
01034 {
01035 aFileName.sprintf("%ld.%d.", (long)time(0), getpid());
01036 aFileName += KApplication::randomString(5);
01037 }
01038
01039 if (!suffix_regex)
01040 suffix_regex_sd.setObject(suffix_regex, new QRegExp(":2,?R?S?$"));
01041
01042 aFileName.truncate(aFileName.findRev(*suffix_regex));
01043
01044
01045 if (! ((status & KMMsgStatusNew) || (status & KMMsgStatusUnread)) )
01046 {
01047 QString suffix( ":2," );
01048 if (status & KMMsgStatusReplied)
01049 suffix += "RS";
01050 else
01051 suffix += "S";
01052 aFileName += suffix;
01053 }
01054
01055 return aFileName;
01056 }
01057
01058
01059 QString KMFolderMaildir::moveInternal(const QString& oldLoc, const QString& newLoc, KMMsgInfo *mi)
01060 {
01061 QString filename(mi->fileName());
01062 QString ret(moveInternal(oldLoc, newLoc, filename, mi->status()));
01063
01064 if (filename != mi->fileName())
01065 mi->setFileName(filename);
01066
01067 return ret;
01068 }
01069
01070
01071 QString KMFolderMaildir::moveInternal(const QString& oldLoc, const QString& newLoc, QString& aFileName, KMMsgStatus status)
01072 {
01073 QString dest(newLoc);
01074
01075 while (QFile::exists(dest))
01076 {
01077 aFileName = constructValidFileName( QString(), status );
01078
01079 QFileInfo fi(dest);
01080 dest = fi.dirPath(true) + "/" + aFileName;
01081 setDirty( true );
01082 }
01083
01084 QDir d;
01085 if (d.rename(oldLoc, dest) == false)
01086 return QString::null;
01087 else
01088 return dest;
01089 }
01090
01091
01092 void KMFolderMaildir::msgStatusChanged(const KMMsgStatus oldStatus,
01093 const KMMsgStatus newStatus, int idx)
01094 {
01095
01096 needsCompact = true;
01097
01098 KMFolderIndex::msgStatusChanged(oldStatus, newStatus, idx);
01099 }
01100
01101
01102 Q_INT64 KMFolderMaildir::doFolderSize() const
01103 {
01104 if ( mCurrentlyCheckingFolderSize )
01105 {
01106 return -1;
01107 }
01108 mCurrentlyCheckingFolderSize = true;
01109
01110 KFileItemList list;
01111 KFileItem *item = 0;
01112 item = new KFileItem( S_IFDIR, -1, location() + "/cur" );
01113 list.append( item );
01114 item = new KFileItem( S_IFDIR, -1, location() + "/new" );
01115 list.append( item );
01116 item = new KFileItem( S_IFDIR, -1, location() + "/tmp" );
01117 list.append( item );
01118 s_DirSizeJobQueue.append(
01119 qMakePair( QGuardedPtr<const KMFolderMaildir>( this ), list ) );
01120
01121
01122
01123 if ( s_DirSizeJobQueue.size() == 1 )
01124 {
01125
01126
01127 KDirSize* job = KDirSize::dirSizeJob( list );
01128 connect( job, SIGNAL( result( KIO::Job* ) ),
01129 this, SLOT( slotDirSizeJobResult( KIO::Job* ) ) );
01130 }
01131
01132 return -1;
01133 }
01134
01135 void KMFolderMaildir::slotDirSizeJobResult( KIO::Job* job )
01136 {
01137 mCurrentlyCheckingFolderSize = false;
01138 KDirSize * dirsize = dynamic_cast<KDirSize*>( job );
01139 if ( dirsize && ! dirsize->error() )
01140 {
01141 mSize = dirsize->totalSize();
01142
01143
01144 emit folderSizeChanged();
01145 }
01146
01147 s_DirSizeJobQueue.pop_front();
01148
01149
01150 while ( s_DirSizeJobQueue.size() > 0 )
01151 {
01152 DirSizeJobQueueEntry entry = s_DirSizeJobQueue.first();
01153
01154 if ( entry.first )
01155 {
01156
01157
01158
01159 KDirSize* job = KDirSize::dirSizeJob( entry.second );
01160 connect( job, SIGNAL( result( KIO::Job* ) ),
01161 entry.first, SLOT( slotDirSizeJobResult( KIO::Job* ) ) );
01162 break;
01163 }
01164 else
01165 {
01166
01167 s_DirSizeJobQueue.pop_front();
01168 }
01169 }
01170 }
01171
01172 #include "kmfoldermaildir.moc"