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