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