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