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