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