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