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