kmail Library API Documentation

kmfoldermaildir.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*- 00002 // kmfoldermaildir.cpp 00003 // Author: Kurt Granroth <granroth@kde.org> 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; // already open 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()) // test if contents file has changed 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+"); // index file 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 //readConfig(); 00144 00145 return rc; 00146 } 00147 00148 00149 //----------------------------------------------------------------------------- 00150 int KMFolderMaildir::create(bool imap) 00151 { 00152 int rc; 00153 int old_umask; 00154 00155 assert(!folder()->name().isEmpty()); 00156 assert(mOpenCount == 0); 00157 00158 // Make sure that neither a new, cur or tmp subfolder exists already. 00159 QFileInfo dirinfo; 00160 dirinfo.setFile(location() + "/new"); 00161 if (dirinfo.exists()) return 1; 00162 dirinfo.setFile(location() + "/cur"); 00163 if (dirinfo.exists()) return 1; 00164 dirinfo.setFile(location() + "/tmp"); 00165 if (dirinfo.exists()) return 1; 00166 00167 // create the maildir directory structure 00168 if (::mkdir(QFile::encodeName(location()), S_IRWXU) > 0) 00169 { 00170 kdDebug(5006) << "Could not create " << location() << " maildir" << endl; 00171 return errno; 00172 } 00173 if (::mkdir(QFile::encodeName(location() + "/new"), S_IRWXU) > 0) 00174 { 00175 kdDebug(5006) << "Could not create " << location() << "/new" << endl; 00176 return errno; 00177 } 00178 if (::mkdir(QFile::encodeName(location() + "/cur"), S_IRWXU) > 0) 00179 { 00180 kdDebug(5006) << "Could not create " << location() << "/cur" << endl; 00181 return errno; 00182 } 00183 if (::mkdir(QFile::encodeName(location() + "/tmp"), S_IRWXU) > 0) 00184 { 00185 kdDebug(5006) << "Could not create " << location() << "/new" << endl; 00186 return errno; 00187 } 00188 00189 if (!folder()->path().isEmpty()) 00190 { 00191 old_umask = umask(077); 00192 mIndexStream = fopen(QFile::encodeName(indexLocation()), "w+"); //sven; open RW 00193 updateIndexStreamPtr(TRUE); 00194 umask(old_umask); 00195 00196 if (!mIndexStream) return errno; 00197 fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC); 00198 } 00199 else 00200 { 00201 mAutoCreateIndex = FALSE; 00202 } 00203 00204 mOpenCount++; 00205 mChanged = FALSE; 00206 if (imap) { 00207 readConfig(); 00208 mUnreadMsgs = -1; 00209 } 00210 00211 rc = writeIndex(); 00212 return rc; 00213 } 00214 00215 00216 //----------------------------------------------------------------------------- 00217 void KMFolderMaildir::close(bool aForced) 00218 { 00219 if (mOpenCount <= 0) return; 00220 if (mOpenCount > 0) mOpenCount--; 00221 00222 if (mOpenCount > 0 && !aForced) return; 00223 00224 #if 0 // removed hack that prevented closing system folders (see kmail-devel discussion about mail expiring) 00225 if ( (folder() != kmkernel->inboxFolder()) 00226 && folder()->isSystemFolder() && !aForced) 00227 { 00228 mOpenCount = 1; 00229 return; 00230 } 00231 #endif 00232 00233 if (mAutoCreateIndex) 00234 { 00235 updateIndex(); 00236 writeConfig(); 00237 } 00238 00239 mMsgList.clear(TRUE); 00240 00241 if (mIndexStream) { 00242 fclose(mIndexStream); 00243 updateIndexStreamPtr(TRUE); 00244 } 00245 00246 mOpenCount = 0; 00247 mIndexStream = 0; 00248 mUnreadMsgs = -1; 00249 00250 mMsgList.reset(INIT_MSGS); 00251 } 00252 00253 //----------------------------------------------------------------------------- 00254 void KMFolderMaildir::sync() 00255 { 00256 if (mOpenCount > 0) 00257 if (!mIndexStream || fsync(fileno(mIndexStream))) { 00258 kmkernel->emergencyExit( i18n("Could not sync maildir folder.") ); 00259 } 00260 } 00261 00262 //----------------------------------------------------------------------------- 00263 int KMFolderMaildir::expungeContents() 00264 { 00265 // nuke all messages in this folder now 00266 QDir d(location() + "/new"); 00267 // d.setFilter(QDir::Files); coolo: QFile::remove returns false for non-files 00268 QStringList files(d.entryList()); 00269 QStringList::ConstIterator it(files.begin()); 00270 for ( ; it != files.end(); ++it) 00271 QFile::remove(d.filePath(*it)); 00272 00273 d.setPath(location() + "/cur"); 00274 files = d.entryList(); 00275 for (it = files.begin(); it != files.end(); ++it) 00276 QFile::remove(d.filePath(*it)); 00277 00278 return 0; 00279 } 00280 00281 int KMFolderMaildir::compact( unsigned int startIndex, int nbMessages, const QStringList& entryList, bool& done ) 00282 { 00283 QString subdirNew(location() + "/new/"); 00284 QString subdirCur(location() + "/cur/"); 00285 00286 unsigned int stopIndex = nbMessages == -1 ? mMsgList.count() : 00287 QMIN( mMsgList.count(), startIndex + nbMessages ); 00288 //kdDebug(5006) << "KMFolderMaildir: compacting from " << startIndex << " to " << stopIndex << endl; 00289 for(unsigned int idx = startIndex; idx < stopIndex; ++idx) { 00290 KMMsgInfo* mi = (KMMsgInfo*)mMsgList.at(idx); 00291 if (!mi) 00292 continue; 00293 00294 QString filename(mi->fileName()); 00295 if (filename.isEmpty()) 00296 continue; 00297 00298 // first, make sure this isn't in the 'new' subdir 00299 if ( entryList.contains( filename ) ) 00300 moveInternal(subdirNew + filename, subdirCur + filename, mi); 00301 00302 // construct a valid filename. if it's already valid, then 00303 // nothing happens 00304 constructValidFileName(filename, mi->status()); 00305 00306 // if the name changed, then we need to update the actual filename 00307 if (filename != mi->fileName()) 00308 { 00309 moveInternal(subdirCur + mi->fileName(), subdirCur + filename, mi); 00310 mi->setFileName(filename); 00311 setDirty( true ); 00312 } 00313 00314 #if 0 00315 // we can't have any New messages at this point 00316 if (mi->isNew()) 00317 { 00318 mi->setStatus(KMMsgStatusUnread); 00319 setDirty( true ); 00320 } 00321 #endif 00322 } 00323 done = ( stopIndex == mMsgList.count() ); 00324 return 0; 00325 } 00326 00327 //----------------------------------------------------------------------------- 00328 int KMFolderMaildir::compact( bool silent ) 00329 { 00330 KMail::MaildirCompactionJob* job = new KMail::MaildirCompactionJob( folder(), true /*immediate*/ ); 00331 int rc = job->executeNow( silent ); 00332 // Note that job autodeletes itself. 00333 return rc; 00334 } 00335 00336 //------------------------------------------------------------- 00337 FolderJob* 00338 KMFolderMaildir::doCreateJob( KMMessage *msg, FolderJob::JobType jt, 00339 KMFolder *folder, QString, const AttachmentStrategy* ) const 00340 { 00341 MaildirJob *job = new MaildirJob( msg, jt, folder ); 00342 job->setParentFolder( this ); 00343 return job; 00344 } 00345 00346 //------------------------------------------------------------- 00347 FolderJob* 00348 KMFolderMaildir::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets, 00349 FolderJob::JobType jt, KMFolder *folder ) const 00350 { 00351 MaildirJob *job = new MaildirJob( msgList, sets, jt, folder ); 00352 job->setParentFolder( this ); 00353 return job; 00354 } 00355 00356 //------------------------------------------------------------- 00357 int KMFolderMaildir::addMsg(KMMessage* aMsg, int* index_return) 00358 { 00359 /* 00360 QFile fileD0( "testdat_xx-kmfoldermaildir-0" ); 00361 if( fileD0.open( IO_WriteOnly ) ) { 00362 QDataStream ds( &fileD0 ); 00363 ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() ); 00364 fileD0.close(); // If data is 0 we just create a zero length file. 00365 } 00366 */ 00367 if (!canAddMsgNow(aMsg, index_return)) return 0; 00368 00369 long len; 00370 unsigned long size; 00371 bool opened = FALSE; 00372 KMFolder* msgParent; 00373 QCString msgText; 00374 int idx(-1); 00375 int rc; 00376 00377 // take message out of the folder it is currently in, if any 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()) // This might be added by 00390 aMsg->removeHeaderField("Content-Type"); // the line above 00391 msgText = aMsg->asString(); 00392 len = msgText.length(); 00393 00394 if (len <= 0) 00395 { 00396 kdDebug(5006) << "Message added to folder `" << name() << "' contains no data. Ignoring it." << endl; 00397 return 0; 00398 } 00399 00400 // make sure the filename has the correct extension 00401 QString filename(aMsg->fileName()); 00402 constructValidFileName(filename, aMsg->status()); 00403 00404 QString tmp_file(location() + "/tmp/"); 00405 tmp_file += filename; 00406 00407 if (!KPIM::kCStringToFile(msgText, tmp_file, false, false, false)) 00408 kmkernel->emergencyExit( "" ); // KPIM::kCStringToFile already showed an errormessage 00409 00410 QFile file(tmp_file); 00411 size = msgText.length(); 00412 00413 if (!isOpened()) 00414 { 00415 opened = TRUE; 00416 rc = open(); 00417 kdDebug(5006) << "KMFolderMaildir::addMsg-open: " << rc << " of folder: " << label() << endl; 00418 if (rc) return rc; 00419 } 00420 00421 // now move the file to the correct location 00422 QString new_loc(location() + "/cur/"); 00423 new_loc += filename; 00424 if (moveInternal(tmp_file, new_loc, filename, aMsg->status()).isNull()) 00425 { 00426 file.remove(); 00427 if (opened) close(); 00428 return -1; 00429 } 00430 00431 if (msgParent) 00432 if (idx >= 0) msgParent->take(idx); 00433 00434 if (filename != aMsg->fileName()) 00435 aMsg->setFileName(filename); 00436 00437 if (aMsg->isUnread() || aMsg->isNew() || folder() == kmkernel->outboxFolder()) 00438 { 00439 if (mUnreadMsgs == -1) 00440 mUnreadMsgs = 1; 00441 else 00442 ++mUnreadMsgs; 00443 emit numUnreadMsgsChanged( folder() ); 00444 } 00445 ++mTotalMsgs; 00446 00447 // store information about the position in the folder file in the message 00448 aMsg->setParent(folder()); 00449 aMsg->setMsgSize(size); 00450 idx = mMsgList.append(&aMsg->toMsgBase()); 00451 if (aMsg->getMsgSerNum() <= 0) 00452 aMsg->setMsgSerNum(); 00453 00454 // write index entry if desired 00455 if (mAutoCreateIndex) 00456 { 00457 assert(mIndexStream != 0); 00458 clearerr(mIndexStream); 00459 fseek(mIndexStream, 0, SEEK_END); 00460 off_t revert = ftell(mIndexStream); 00461 00462 int len; 00463 KMMsgBase * mb = &aMsg->toMsgBase(); 00464 const uchar *buffer = mb->asIndexString(len); 00465 fwrite(&len,sizeof(len), 1, mIndexStream); 00466 mb->setIndexOffset( ftell(mIndexStream) ); 00467 mb->setIndexLength( len ); 00468 if(fwrite(buffer, len, 1, mIndexStream) != 1) 00469 kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl; 00470 00471 fflush(mIndexStream); 00472 int error = ferror(mIndexStream); 00473 00474 error |= appendtoMsgDict(idx); 00475 00476 if (error) { 00477 kdDebug(5006) << "Error: Could not add message to folder (No space left on device?)" << endl; 00478 if (ftell(mIndexStream) > revert) { 00479 kdDebug(5006) << "Undoing changes" << endl; 00480 truncate( QFile::encodeName(indexLocation()), revert ); 00481 } 00482 kmkernel->emergencyExit(i18n("KMFolderMaildir::addMsg: abnormally terminating to prevent data loss.")); 00483 // exit(1); // don't ever use exit(), use the above! 00484 00485 /* This code may not be 100% reliable 00486 bool busy = kmkernel->kbp()->isBusy(); 00487 if (busy) kmkernel->kbp()->idle(); 00488 KMessageBox::sorry(0, 00489 i18n("Unable to add message to folder.\n" 00490 "(No space left on device or insufficient quota?)\n" 00491 "Free space and sufficient quota are required to continue safely.")); 00492 if (busy) kmkernel->kbp()->busy(); 00493 if (opened) close(); 00494 */ 00495 return error; 00496 } 00497 } 00498 00499 // some "paper work" 00500 if (index_return) 00501 *index_return = idx; 00502 00503 emitMsgAddedSignals(idx); 00504 needsCompact = true; 00505 00506 if (opened) close(); 00507 /* 00508 QFile fileD1( "testdat_xx-kmfoldermaildir-1" ); 00509 if( fileD1.open( IO_WriteOnly ) ) { 00510 QDataStream ds( &fileD1 ); 00511 ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() ); 00512 fileD1.close(); // If data is 0 we just create a zero length file. 00513 } 00514 */ 00515 return 0; 00516 } 00517 00518 KMMessage* KMFolderMaildir::readMsg(int idx) 00519 { 00520 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx]; 00521 KMMessage *msg = new KMMessage(*mi); 00522 00523 msg->fromDwString(getDwString(idx)); 00524 mMsgList.set(idx,&msg->toMsgBase()); 00525 return msg; 00526 } 00527 00528 DwString KMFolderMaildir::getDwString(int idx) 00529 { 00530 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx]; 00531 QString abs_file(location() + "/cur/"); 00532 abs_file += mi->fileName(); 00533 QFileInfo fi( abs_file ); 00534 00535 if (fi.exists() && fi.isFile() && fi.isWritable() && fi.size() > 0) 00536 { 00537 FILE* stream = fopen(QFile::encodeName(abs_file), "r+"); 00538 if (stream) { 00539 size_t msgSize = fi.size(); 00540 char* msgText = new char[ msgSize + 1 ]; 00541 fread(msgText, msgSize, 1, stream); 00542 fclose( stream ); 00543 msgText[msgSize] = '\0'; 00544 size_t newMsgSize = crlf2lf( msgText, msgSize ); 00545 DwString str; 00546 // the DwString takes possession of msgText, so we must not delete it 00547 str.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize ); 00548 return str; 00549 } 00550 } 00551 kdDebug(5006) << "Could not open file r+ " << abs_file << endl; 00552 return DwString(); 00553 } 00554 00555 00556 QCString& KMFolderMaildir::getMsgString(int idx, QCString& mDest) 00557 { 00558 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx]; 00559 00560 assert(mi!=0); 00561 00562 QString abs_file(location() + "/cur/"); 00563 abs_file += mi->fileName(); 00564 00565 if (QFile::exists(abs_file) == false) 00566 { 00567 kdDebug(5006) << "The " << abs_file << " file doesn't exist!" << endl; 00568 return mDest; 00569 } 00570 00571 QFileInfo fi( abs_file ); 00572 mDest.resize(fi.size()+2); 00573 mDest = KPIM::kFileToString(abs_file, false, false); 00574 size_t newMsgSize = crlf2lf( mDest.data(), fi.size() ); 00575 mDest[newMsgSize] = '\0'; 00576 return mDest; 00577 } 00578 00579 void KMFolderMaildir::readFileHeaderIntern(const QString& dir, const QString& file, KMMsgStatus status) 00580 { 00581 // we keep our current directory to restore it later 00582 char path_buffer[PATH_MAX]; 00583 ::getcwd(path_buffer, PATH_MAX - 1); 00584 ::chdir(QFile::encodeName(dir)); 00585 00586 // messages in the 'cur' directory are Read by default.. but may 00587 // actually be some other state (but not New) 00588 if (status == KMMsgStatusRead) 00589 { 00590 if (file.find(":2,") == -1) 00591 status = KMMsgStatusUnread; 00592 else if (file.right(5) == ":2,RS") 00593 status |= KMMsgStatusReplied; 00594 } 00595 00596 // open the file and get a pointer to it 00597 QFile f(file); 00598 if ( f.open( IO_ReadOnly ) == false ) { 00599 kdWarning(5006) << "The file '" << QFile::encodeName(dir) << "/" << file 00600 << "' could not be opened for reading the message. " 00601 "Please check ownership and permissions." 00602 << endl; 00603 return; 00604 } 00605 00606 char line[MAX_LINE]; 00607 bool atEof = false; 00608 bool inHeader = true; 00609 QCString *lastStr = 0; 00610 00611 QCString dateStr, fromStr, toStr, subjStr; 00612 QCString xmarkStr, replyToIdStr, msgIdStr, referencesStr; 00613 QCString statusStr, replyToAuxIdStr, uidStr; 00614 00615 // iterate through this file until done 00616 while (!atEof) 00617 { 00618 // if the end of the file has been reached or if there was an error 00619 if ( f.atEnd() || ( -1 == f.readLine(line, MAX_LINE) ) ) 00620 atEof = true; 00621 00622 // are we done with this file? if so, compile our info and store 00623 // it in a KMMsgInfo object 00624 if (atEof || !inHeader) 00625 { 00626 msgIdStr = msgIdStr.stripWhiteSpace(); 00627 if( !msgIdStr.isEmpty() ) { 00628 int rightAngle; 00629 rightAngle = msgIdStr.find( '>' ); 00630 if( rightAngle != -1 ) 00631 msgIdStr.truncate( rightAngle + 1 ); 00632 } 00633 00634 replyToIdStr = replyToIdStr.stripWhiteSpace(); 00635 if( !replyToIdStr.isEmpty() ) { 00636 int rightAngle; 00637 rightAngle = replyToIdStr.find( '>' ); 00638 if( rightAngle != -1 ) 00639 replyToIdStr.truncate( rightAngle + 1 ); 00640 } 00641 00642 referencesStr = referencesStr.stripWhiteSpace(); 00643 if( !referencesStr.isEmpty() ) { 00644 int leftAngle, rightAngle; 00645 leftAngle = referencesStr.findRev( '<' ); 00646 if( ( leftAngle != -1 ) 00647 && ( replyToIdStr.isEmpty() || ( replyToIdStr[0] != '<' ) ) ) { 00648 // use the last reference, instead of missing In-Reply-To 00649 replyToIdStr = referencesStr.mid( leftAngle ); 00650 } 00651 00652 // find second last reference 00653 leftAngle = referencesStr.findRev( '<', leftAngle - 1 ); 00654 if( leftAngle != -1 ) 00655 referencesStr = referencesStr.mid( leftAngle ); 00656 rightAngle = referencesStr.findRev( '>' ); 00657 if( rightAngle != -1 ) 00658 referencesStr.truncate( rightAngle + 1 ); 00659 00660 // Store the second to last reference in the replyToAuxIdStr 00661 // It is a good candidate for threading the message below if the 00662 // message In-Reply-To points to is not kept in this folder, 00663 // but e.g. in an Outbox 00664 replyToAuxIdStr = referencesStr; 00665 rightAngle = referencesStr.find( '>' ); 00666 if( rightAngle != -1 ) 00667 replyToAuxIdStr.truncate( rightAngle + 1 ); 00668 } 00669 00670 statusStr = statusStr.stripWhiteSpace(); 00671 if (!statusStr.isEmpty()) 00672 { 00673 // only handle those states not determined by the file suffix 00674 if (statusStr[0] == 'S') 00675 status |= KMMsgStatusSent; 00676 else if (statusStr[0] == 'F') 00677 status |= KMMsgStatusForwarded; 00678 else if (statusStr[0] == 'D') 00679 status |= KMMsgStatusDeleted; 00680 else if (statusStr[0] == 'Q') 00681 status |= KMMsgStatusQueued; 00682 else if (statusStr[0] == 'G') 00683 status |= KMMsgStatusFlag; 00684 } 00685 00686 KMMsgInfo *mi = new KMMsgInfo(folder()); 00687 mi->init( subjStr.stripWhiteSpace(), 00688 fromStr.stripWhiteSpace(), 00689 toStr.stripWhiteSpace(), 00690 0, status, 00691 xmarkStr.stripWhiteSpace(), 00692 replyToIdStr, replyToAuxIdStr, msgIdStr, 00693 file.local8Bit(), 00694 KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown, 00695 KMMsgMDNStateUnknown, f.size() ); 00696 00697 dateStr = dateStr.stripWhiteSpace(); 00698 if (!dateStr.isEmpty()) 00699 mi->setDate(dateStr); 00700 if ( !uidStr.isEmpty() ) 00701 mi->setUID( uidStr.toULong() ); 00702 mi->setDirty(false); 00703 mMsgList.append(mi); 00704 00705 // if this is a New file and is in 'new', we move it to 'cur' 00706 if (status & KMMsgStatusNew) 00707 { 00708 QString newDir(location() + "/new/"); 00709 QString curDir(location() + "/cur/"); 00710 moveInternal(newDir + file, curDir + file, mi); 00711 } 00712 00713 break; 00714 } 00715 00716 // Is this a long header line? 00717 if (inHeader && line[0] == '\t' || line[0] == ' ') 00718 { 00719 int i = 0; 00720 while (line[i] == '\t' || line[i] == ' ') 00721 i++; 00722 if (line[i] < ' ' && line[i] > 0) 00723 inHeader = false; 00724 else 00725 if (lastStr) 00726 *lastStr += line + i; 00727 } 00728 else 00729 lastStr = 0; 00730 00731 if (inHeader && (line[0] == '\n' || line[0] == '\r')) 00732 inHeader = false; 00733 if (!inHeader) 00734 continue; 00735 00736 if (strncasecmp(line, "Date:", 5) == 0) 00737 { 00738 dateStr = QCString(line+5); 00739 lastStr = &dateStr; 00740 } 00741 else if (strncasecmp(line, "From:", 5) == 0) 00742 { 00743 fromStr = QCString(line+5); 00744 lastStr = &fromStr; 00745 } 00746 else if (strncasecmp(line, "To:", 3) == 0) 00747 { 00748 toStr = QCString(line+3); 00749 lastStr = &toStr; 00750 } 00751 else if (strncasecmp(line, "Subject:", 8) == 0) 00752 { 00753 subjStr = QCString(line+8); 00754 lastStr = &subjStr; 00755 } 00756 else if (strncasecmp(line, "References:", 11) == 0) 00757 { 00758 referencesStr = QCString(line+11); 00759 lastStr = &referencesStr; 00760 } 00761 else if (strncasecmp(line, "Message-Id:", 11) == 0) 00762 { 00763 msgIdStr = QCString(line+11); 00764 lastStr = &msgIdStr; 00765 } 00766 else if (strncasecmp(line, "X-KMail-Mark:", 13) == 0) 00767 { 00768 xmarkStr = QCString(line+13); 00769 } 00770 else if (strncasecmp(line, "X-Status:", 9) == 0) 00771 { 00772 statusStr = QCString(line+9); 00773 } 00774 else if (strncasecmp(line, "In-Reply-To:", 12) == 0) 00775 { 00776 replyToIdStr = QCString(line+12); 00777 lastStr = &replyToIdStr; 00778 } 00779 else if (strncasecmp(line, "X-UID:", 6) == 0) 00780 { 00781 uidStr = QCString(line+6); 00782 lastStr = &uidStr; 00783 } 00784 00785 } 00786 00787 if (status & KMMsgStatusNew || status & KMMsgStatusUnread || 00788 (folder() == kmkernel->outboxFolder())) 00789 { 00790 mUnreadMsgs++; 00791 if (mUnreadMsgs == 0) ++mUnreadMsgs; 00792 } 00793 00794 ::chdir(path_buffer); 00795 } 00796 00797 int KMFolderMaildir::createIndexFromContents() 00798 { 00799 mUnreadMsgs = 0; 00800 00801 mMsgList.clear(true); 00802 mMsgList.reset(INIT_MSGS); 00803 00804 mChanged = false; 00805 00806 // first, we make sure that all the directories are here as they 00807 // should be 00808 QFileInfo dirinfo; 00809 00810 dirinfo.setFile(location() + "/new"); 00811 if (!dirinfo.exists() || !dirinfo.isDir()) 00812 { 00813 kdDebug(5006) << "Directory " << location() << "/new doesn't exist or is a file"<< endl; 00814 return 1; 00815 } 00816 QDir newDir(location() + "/new"); 00817 newDir.setFilter(QDir::Files); 00818 00819 dirinfo.setFile(location() + "/cur"); 00820 if (!dirinfo.exists() || !dirinfo.isDir()) 00821 { 00822 kdDebug(5006) << "Directory " << location() << "/cur doesn't exist or is a file"<< endl; 00823 return 1; 00824 } 00825 QDir curDir(location() + "/cur"); 00826 curDir.setFilter(QDir::Files); 00827 00828 // then, we look for all the 'cur' files 00829 const QFileInfoList *list = curDir.entryInfoList(); 00830 QFileInfoListIterator it(*list); 00831 QFileInfo *fi; 00832 00833 while ((fi = it.current())) 00834 { 00835 readFileHeaderIntern(curDir.path(), fi->fileName(), KMMsgStatusRead); 00836 ++it; 00837 } 00838 00839 // then, we look for all the 'new' files 00840 list = newDir.entryInfoList(); 00841 it = *list; 00842 00843 while ((fi=it.current())) 00844 { 00845 readFileHeaderIntern(newDir.path(), fi->fileName(), KMMsgStatusNew); 00846 ++it; 00847 } 00848 00849 if (autoCreateIndex()) 00850 { 00851 emit statusMsg(i18n("Writing index file")); 00852 writeIndex(); 00853 } 00854 else mHeaderOffset = 0; 00855 00856 correctUnreadMsgsCount(); 00857 00858 if (kmkernel->outboxFolder() == folder() && count() > 0) 00859 KMessageBox::information(0, i18n("Your outbox contains messages which were " 00860 "most-likely not created by KMail;\nplease remove them from there if you " 00861 "do not want KMail to send them.")); 00862 00863 needsCompact = true; 00864 00865 if (folder()->parent()) 00866 folder()->parent()->manager()->invalidateFolder(kmkernel->msgDict(), folder()); 00867 return 0; 00868 } 00869 00870 KMFolderIndex::IndexStatus KMFolderMaildir::indexStatus() 00871 { 00872 QFileInfo new_info(location() + "/new"); 00873 QFileInfo cur_info(location() + "/cur"); 00874 QFileInfo index_info(indexLocation()); 00875 00876 if (!index_info.exists()) 00877 return KMFolderIndex::IndexMissing; 00878 00879 // Check whether the directories are more than 5 seconds newer than the index 00880 // file. The 5 seconds are added to reduce the number of false alerts due 00881 // to slightly out of sync clocks of the NFS server and the local machine. 00882 return ((new_info.lastModified() > index_info.lastModified().addSecs(5)) || 00883 (cur_info.lastModified() > index_info.lastModified().addSecs(5))) 00884 ? KMFolderIndex::IndexTooOld 00885 : KMFolderIndex::IndexOk; 00886 } 00887 00888 //----------------------------------------------------------------------------- 00889 void KMFolderMaildir::removeMsg(int idx, bool) 00890 { 00891 KMMsgBase* msg = mMsgList[idx]; 00892 if (!msg || !msg->fileName()) return; 00893 00894 removeFile(msg->fileName()); 00895 00896 KMFolderIndex::removeMsg(idx); 00897 } 00898 00899 //----------------------------------------------------------------------------- 00900 KMMessage* KMFolderMaildir::take(int idx) 00901 { 00902 // first, we do the high-level stuff.. then delete later 00903 KMMessage *msg = KMFolderIndex::take(idx); 00904 00905 if (!msg || !msg->fileName()) return 0; 00906 00907 if (removeFile(msg->fileName())) 00908 return msg; 00909 else 00910 return 0; 00911 } 00912 00913 bool KMFolderMaildir::removeFile(const QString& filename) 00914 { 00915 // we need to look in both 'new' and 'cur' since it's possible to 00916 // delete a message before the folder is compacted. since the file 00917 // naming and moving is done in ::compact, we can't assume any 00918 // location at this point 00919 QCString abs_file(QFile::encodeName(location() + "/cur/")); 00920 abs_file += QFile::encodeName(filename); 00921 00922 if (::unlink( abs_file ) == 0) 00923 return true; 00924 00925 if (errno == ENOENT) {// doesn't exist 00926 00927 abs_file = QFile::encodeName(location() + "/new/"); 00928 abs_file += QFile::encodeName(filename); 00929 00930 if (::unlink( abs_file ) == 0) 00931 return true; 00932 00933 } 00934 00935 kdDebug(5006) << "Can't delete " << abs_file << " " << perror << endl; 00936 return false; 00937 } 00938 00939 //----------------------------------------------------------------------------- 00940 int KMFolderMaildir::removeContents() 00941 { 00942 if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()+ "/new/"), 0)) 00943 return 1; 00944 if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()+ "/cur/"), 0)) 00945 return 1; 00946 if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()+ "/tmp/"), 0)) 00947 return 1; 00948 00949 /* The subdirs are removed now. Check if there is anything else in the dir 00950 * and only if not delete the dir itself. The user could have data stored 00951 * that would otherwise be deleted. */ 00952 QDir dir(location()); 00953 if ( dir.count() == 2 ) { // only . and .. 00954 if (!KIO::NetAccess::del(KURL::fromPathOrURL(location()), 0)) 00955 return 1; 00956 } 00957 return 0; 00958 } 00959 00960 static QRegExp *suffix_regex = 0; 00961 static KStaticDeleter<QRegExp> suffix_regex_sd; 00962 00963 //----------------------------------------------------------------------------- 00964 QString KMFolderMaildir::constructValidFileName(QString& aFileName, KMMsgStatus status) 00965 { 00966 if (aFileName.isEmpty()) 00967 { 00968 aFileName.sprintf("%ld.%d.", (long)time(0), getpid()); 00969 aFileName += KApplication::randomString(5); 00970 } 00971 00972 if (!suffix_regex) 00973 suffix_regex_sd.setObject(suffix_regex, new QRegExp(":2,?R?S?$")); 00974 00975 aFileName.truncate(aFileName.findRev(*suffix_regex)); 00976 00977 QString suffix; 00978 if (! ((status & KMMsgStatusNew) || (status & KMMsgStatusUnread)) ) 00979 { 00980 suffix += ":2,"; 00981 if (status & KMMsgStatusReplied) 00982 suffix += "RS"; 00983 else 00984 suffix += "S"; 00985 } 00986 00987 aFileName += suffix; 00988 00989 return aFileName; 00990 } 00991 00992 //----------------------------------------------------------------------------- 00993 QString KMFolderMaildir::moveInternal(const QString& oldLoc, const QString& newLoc, KMMsgInfo *mi) 00994 { 00995 QString filename(mi->fileName()); 00996 QString ret(moveInternal(oldLoc, newLoc, filename, mi->status())); 00997 00998 if (filename != mi->fileName()) 00999 mi->setFileName(filename); 01000 01001 return ret; 01002 } 01003 01004 //----------------------------------------------------------------------------- 01005 QString KMFolderMaildir::moveInternal(const QString& oldLoc, const QString& newLoc, QString& aFileName, KMMsgStatus status) 01006 { 01007 QString dest(newLoc); 01008 // make sure that our destination filename doesn't already exist 01009 while (QFile::exists(dest)) 01010 { 01011 aFileName = ""; 01012 constructValidFileName(aFileName, status); 01013 01014 QFileInfo fi(dest); 01015 dest = fi.dirPath(true) + "/" + aFileName; 01016 setDirty( true ); 01017 } 01018 01019 QDir d; 01020 if (d.rename(oldLoc, dest) == false) 01021 return QString::null; 01022 else 01023 return dest; 01024 } 01025 01026 //----------------------------------------------------------------------------- 01027 void KMFolderMaildir::msgStatusChanged(const KMMsgStatus oldStatus, 01028 const KMMsgStatus newStatus, int idx) 01029 { 01030 // if the status of any message changes, then we need to compact 01031 needsCompact = true; 01032 01033 KMFolderIndex::msgStatusChanged(oldStatus, newStatus, idx); 01034 } 01035 01036 #include "kmfoldermaildir.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:48 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003