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