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