kmail Library API Documentation

kmfoldermbox.cpp

00001 // -*- c-basic-offset: 2 -*- 00002 // kmfoldermbox.cpp 00003 // Author: Stefan Taferner <taferner@alpin.or.at> 00004 00005 #include <config.h> 00006 #include <qfileinfo.h> 00007 #include <qregexp.h> 00008 00009 #include "kmfoldermbox.h" 00010 #include "kmfoldermgr.h" 00011 #include "undostack.h" 00012 #include "kcursorsaver.h" 00013 00014 #include <kdebug.h> 00015 #include <klocale.h> 00016 #include <kmessagebox.h> 00017 #include <knotifyclient.h> 00018 #include <kprocess.h> 00019 #include <kconfig.h> 00020 00021 #include <stdio.h> 00022 #include <errno.h> 00023 #include <assert.h> 00024 #include <unistd.h> 00025 00026 #ifdef HAVE_FCNTL_H 00027 #include <fcntl.h> 00028 #endif 00029 00030 #include <stdlib.h> 00031 #include <sys/types.h> 00032 #include <sys/stat.h> 00033 #include <sys/file.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 // Regular expression to find the line that seperates messages in a mail 00043 // folder: 00044 #define MSG_SEPERATOR_START "From " 00045 #define MSG_SEPERATOR_REGEX "^From .*[0-9][0-9]:[0-9][0-9].*$" 00046 static short msgSepLen = strlen(MSG_SEPERATOR_START); 00047 00048 00049 //----------------------------------------------------------------------------- 00050 KMFolderMbox::KMFolderMbox(KMFolderDir* aParent, const QString& aName) 00051 : KMFolderIndex(aParent, aName) 00052 { 00053 mStream = 0; 00054 mFilesLocked = false; 00055 mLockType = lock_none; 00056 } 00057 00058 00059 //----------------------------------------------------------------------------- 00060 KMFolderMbox::~KMFolderMbox() 00061 { 00062 if (mOpenCount>0) close(true); 00063 if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed(this); 00064 } 00065 00066 //----------------------------------------------------------------------------- 00067 int KMFolderMbox::open() 00068 { 00069 int rc = 0; 00070 00071 mOpenCount++; 00072 if (mOpenCount > 1) return 0; // already open 00073 00074 assert(!name().isEmpty()); 00075 00076 mFilesLocked = false; 00077 mStream = fopen(QFile::encodeName(location()), "r+"); // messages file 00078 if (!mStream) 00079 { 00080 KNotifyClient::event( 0, "warning", 00081 i18n("Cannot open file \"%1\":\n%2").arg(location()).arg(strerror(errno))); 00082 kdDebug(5006) << "Cannot open folder `" << location() << "': " << strerror(errno) << endl; 00083 mOpenCount = 0; 00084 return errno; 00085 } 00086 00087 lock(); 00088 00089 if (!path().isEmpty()) 00090 { 00091 KMFolderIndex::IndexStatus index_status = indexStatus(); 00092 // test if index file exists and is up-to-date 00093 if (KMFolderIndex::IndexOk != index_status) 00094 { 00095 // only show a warning if the index file exists, otherwise it can be 00096 // silently regenerated 00097 if (KMFolderIndex::IndexTooOld == index_status) { 00098 QString msg = i18n("<qt><p>The index of folder '%2' seems " 00099 "to be out of date. To prevent message " 00100 "corruption the index will be " 00101 "regenerated. As a result deleted " 00102 "messages might reappear and status " 00103 "flags might be lost.</p>" 00104 "<p>Please read the corresponding entry " 00105 "in the <a href=\"%1\">FAQ section of the manual " 00106 "of KMail</a> for " 00107 "information about how to prevent this " 00108 "problem from happening again.</p></qt>") 00109 .arg("help:/kmail/faq.html#faq-index-regeneration") 00110 .arg(name()); 00111 // When KMail is starting up we have to show a non-blocking message 00112 // box so that the initialization can continue. We don't show a 00113 // queued message box when KMail isn't starting up because queued 00114 // message boxes don't have a "Don't ask again" checkbox. 00115 if (kmkernel->startingUp()) 00116 { 00117 KConfigGroup configGroup( KMKernel::config(), "Notification Messages" ); 00118 bool showMessage = 00119 configGroup.readBoolEntry( "showIndexRegenerationMessage", true ); 00120 if (showMessage) 00121 KMessageBox::queuedMessageBox( 0, KMessageBox::Information, 00122 msg, i18n("Index Out of Date"), 00123 KMessageBox::AllowLink ); 00124 } 00125 else 00126 { 00127 KCursorSaver idle(KBusyPtr::idle()); 00128 KMessageBox::information( 0, msg, i18n("Index Out of Date"), 00129 "showIndexRegenerationMessage", 00130 KMessageBox::AllowLink ); 00131 } 00132 } 00133 QString str; 00134 mIndexStream = 0; 00135 str = i18n("Folder `%1' changed. Recreating index.") 00136 .arg(name()); 00137 emit statusMsg(str); 00138 } else { 00139 mIndexStream = fopen(QFile::encodeName(indexLocation()), "r+"); // index file 00140 updateIndexStreamPtr(); 00141 } 00142 00143 if (!mIndexStream) 00144 rc = createIndexFromContents(); 00145 else 00146 if (!readIndex()) 00147 rc = createIndexFromContents(); 00148 } 00149 else 00150 { 00151 mAutoCreateIndex = false; 00152 rc = createIndexFromContents(); 00153 } 00154 00155 mChanged = false; 00156 00157 return rc; 00158 } 00159 00160 //---------------------------------------------------------------------------- 00161 int KMFolderMbox::canAccess() 00162 { 00163 assert(!name().isEmpty()); 00164 00165 if (access(QFile::encodeName(location()), R_OK | W_OK) != 0) { 00166 kdDebug(5006) << "KMFolderMbox::access call to access function failed" << endl; 00167 return 1; 00168 } 00169 return 0; 00170 } 00171 00172 //----------------------------------------------------------------------------- 00173 int KMFolderMbox::create(bool imap) 00174 { 00175 int rc; 00176 int old_umask; 00177 00178 Q_UNUSED(imap); 00179 00180 assert(!name().isEmpty()); 00181 assert(mOpenCount == 0); 00182 00183 kdDebug(5006) << "Creating folder " << name() << endl; 00184 if (access(QFile::encodeName(location()), F_OK) == 0) { 00185 kdDebug(5006) << "KMFolderMbox::create call to access function failed." << endl; 00186 kdDebug(5006) << "File:: " << endl; 00187 kdDebug(5006) << "Error " << endl; 00188 return EEXIST; 00189 } 00190 00191 old_umask = umask(077); 00192 mStream = fopen(QFile::encodeName(location()), "w+"); //sven; open RW 00193 umask(old_umask); 00194 00195 if (!mStream) return errno; 00196 00197 if (!path().isEmpty()) 00198 { 00199 old_umask = umask(077); 00200 mIndexStream = fopen(QFile::encodeName(indexLocation()), "w+"); //sven; open RW 00201 updateIndexStreamPtr(true); 00202 umask(old_umask); 00203 00204 if (!mIndexStream) return errno; 00205 } 00206 else 00207 { 00208 mAutoCreateIndex = false; 00209 } 00210 00211 mOpenCount++; 00212 mChanged = false; 00213 00214 rc = writeIndex(); 00215 if (!rc) lock(); 00216 return rc; 00217 } 00218 00219 00220 //----------------------------------------------------------------------------- 00221 void KMFolderMbox::close(bool aForced) 00222 { 00223 if (mOpenCount <= 0 || !mStream) return; 00224 if (mOpenCount > 0) mOpenCount--; 00225 if (mOpenCount > 0 && !aForced) return; 00226 if ((this != kmkernel->inboxFolder()) && isSystemFolder() && !aForced) 00227 { 00228 mOpenCount = 1; 00229 return; 00230 } 00231 00232 if (mAutoCreateIndex) 00233 { 00234 if (KMFolderIndex::IndexOk != indexStatus()) { 00235 kdDebug(5006) << "Critical error: " << location() << 00236 " has been modified by an external application while KMail was running." << endl; 00237 // exit(1); backed out due to broken nfs 00238 } 00239 00240 updateIndex(); 00241 writeConfig(); 00242 } 00243 00244 if (!noContent()) { 00245 if (mStream) unlock(); 00246 mMsgList.clear(true); 00247 00248 if (mStream) fclose(mStream); 00249 if (mIndexStream) { 00250 fclose(mIndexStream); 00251 updateIndexStreamPtr(true); 00252 } 00253 } 00254 mOpenCount = 0; 00255 mStream = 0; 00256 mIndexStream = 0; 00257 mFilesLocked = false; 00258 mUnreadMsgs = -1; 00259 00260 mMsgList.reset(INIT_MSGS); 00261 } 00262 00263 //----------------------------------------------------------------------------- 00264 void KMFolderMbox::sync() 00265 { 00266 if (mOpenCount > 0) 00267 if (!mStream || fsync(fileno(mStream)) || 00268 !mIndexStream || fsync(fileno(mIndexStream))) { 00269 kmkernel->emergencyExit( i18n("Could not sync index file <b>%1</b>: %2").arg( indexLocation() ).arg(errno ? QString::fromLocal8Bit(strerror(errno)) : i18n("Internal error. Please copy down the details and report a bug."))); 00270 } 00271 } 00272 00273 //----------------------------------------------------------------------------- 00274 int KMFolderMbox::lock() 00275 { 00276 int rc; 00277 struct flock fl; 00278 fl.l_type=F_WRLCK; 00279 fl.l_whence=0; 00280 fl.l_start=0; 00281 fl.l_len=0; 00282 fl.l_pid=-1; 00283 QCString cmd_str; 00284 assert(mStream != 0); 00285 mFilesLocked = false; 00286 00287 switch( mLockType ) 00288 { 00289 case FCNTL: 00290 rc = fcntl(fileno(mStream), F_SETLKW, &fl); 00291 00292 if (rc < 0) 00293 { 00294 kdDebug(5006) << "Cannot lock folder `" << location() << "': " 00295 << strerror(errno) << " (" << errno << ")" << endl; 00296 return errno; 00297 } 00298 00299 if (mIndexStream) 00300 { 00301 rc = fcntl(fileno(mIndexStream), F_SETLK, &fl); 00302 00303 if (rc < 0) 00304 { 00305 kdDebug(5006) << "Cannot lock index of folder `" << location() << "': " 00306 << strerror(errno) << " (" << errno << ")" << endl; 00307 rc = errno; 00308 fl.l_type = F_UNLCK; 00309 rc = fcntl(fileno(mIndexStream), F_SETLK, &fl); 00310 return rc; 00311 } 00312 } 00313 break; 00314 00315 case procmail_lockfile: 00316 cmd_str = "lockfile -l20 -r5 "; 00317 if (!mProcmailLockFileName.isEmpty()) 00318 cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName)); 00319 else 00320 cmd_str += QFile::encodeName(KProcess::quote(location() + ".lock")); 00321 00322 rc = system( cmd_str.data() ); 00323 if( rc != 0 ) 00324 { 00325 kdDebug(5006) << "Cannot lock folder `" << location() << "': " 00326 << strerror(rc) << " (" << rc << ")" << endl; 00327 return rc; 00328 } 00329 if( mIndexStream ) 00330 { 00331 cmd_str = "lockfile -l20 -r5 " + QFile::encodeName(KProcess::quote(indexLocation() + ".lock")); 00332 rc = system( cmd_str.data() ); 00333 if( rc != 0 ) 00334 { 00335 kdDebug(5006) << "Cannot lock index of folder `" << location() << "': " 00336 << strerror(rc) << " (" << rc << ")" << endl; 00337 return rc; 00338 } 00339 } 00340 break; 00341 00342 case mutt_dotlock: 00343 cmd_str = "mutt_dotlock " + QFile::encodeName(KProcess::quote(location())); 00344 rc = system( cmd_str.data() ); 00345 if( rc != 0 ) 00346 { 00347 kdDebug(5006) << "Cannot lock folder `" << location() << "': " 00348 << strerror(rc) << " (" << rc << ")" << endl; 00349 return rc; 00350 } 00351 if( mIndexStream ) 00352 { 00353 cmd_str = "mutt_dotlock " + QFile::encodeName(KProcess::quote(indexLocation())); 00354 rc = system( cmd_str.data() ); 00355 if( rc != 0 ) 00356 { 00357 kdDebug(5006) << "Cannot lock index of folder `" << location() << "': " 00358 << strerror(rc) << " (" << rc << ")" << endl; 00359 return rc; 00360 } 00361 } 00362 break; 00363 00364 case mutt_dotlock_privileged: 00365 cmd_str = "mutt_dotlock -p " + QFile::encodeName(KProcess::quote(location())); 00366 rc = system( cmd_str.data() ); 00367 if( rc != 0 ) 00368 { 00369 kdDebug(5006) << "Cannot lock folder `" << location() << "': " 00370 << strerror(rc) << " (" << rc << ")" << endl; 00371 return rc; 00372 } 00373 if( mIndexStream ) 00374 { 00375 cmd_str = "mutt_dotlock -p " + QFile::encodeName(KProcess::quote(indexLocation())); 00376 rc = system( cmd_str.data() ); 00377 if( rc != 0 ) 00378 { 00379 kdDebug(5006) << "Cannot lock index of folder `" << location() << "': " 00380 << strerror(rc) << " (" << rc << ")" << endl; 00381 return rc; 00382 } 00383 } 00384 break; 00385 00386 case lock_none: 00387 default: 00388 break; 00389 } 00390 00391 00392 mFilesLocked = true; 00393 return 0; 00394 } 00395 00396 //------------------------------------------------------------- 00397 FolderJob* 00398 KMFolderMbox::doCreateJob( KMMessage *msg, FolderJob::JobType jt, 00399 KMFolder *folder, QString, const AttachmentStrategy* ) const 00400 { 00401 MboxJob *job = new MboxJob( msg, jt, folder ); 00402 job->setParent( this ); 00403 return job; 00404 } 00405 00406 //------------------------------------------------------------- 00407 FolderJob* 00408 KMFolderMbox::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets, 00409 FolderJob::JobType jt, KMFolder *folder ) const 00410 { 00411 MboxJob *job = new MboxJob( msgList, sets, jt, folder ); 00412 job->setParent( this ); 00413 return job; 00414 } 00415 00416 //----------------------------------------------------------------------------- 00417 int KMFolderMbox::unlock() 00418 { 00419 int rc; 00420 struct flock fl; 00421 fl.l_type=F_UNLCK; 00422 fl.l_whence=0; 00423 fl.l_start=0; 00424 fl.l_len=0; 00425 QCString cmd_str; 00426 00427 assert(mStream != 0); 00428 mFilesLocked = false; 00429 00430 switch( mLockType ) 00431 { 00432 case FCNTL: 00433 if (mIndexStream) fcntl(fileno(mIndexStream), F_SETLK, &fl); 00434 fcntl(fileno(mStream), F_SETLK, &fl); 00435 rc = errno; 00436 break; 00437 00438 case procmail_lockfile: 00439 cmd_str = "rm -f "; 00440 if (!mProcmailLockFileName.isEmpty()) 00441 cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName)); 00442 else 00443 cmd_str += QFile::encodeName(KProcess::quote(location() + ".lock")); 00444 00445 rc = system( cmd_str.data() ); 00446 if( mIndexStream ) 00447 { 00448 cmd_str = "rm -f " + QFile::encodeName(KProcess::quote(indexLocation() + ".lock")); 00449 rc = system( cmd_str.data() ); 00450 } 00451 break; 00452 00453 case mutt_dotlock: 00454 cmd_str = "mutt_dotlock -u " + QFile::encodeName(KProcess::quote(location())); 00455 rc = system( cmd_str.data() ); 00456 if( mIndexStream ) 00457 { 00458 cmd_str = "mutt_dotlock -u " + QFile::encodeName(KProcess::quote(indexLocation())); 00459 rc = system( cmd_str.data() ); 00460 } 00461 break; 00462 00463 case mutt_dotlock_privileged: 00464 cmd_str = "mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(location())); 00465 rc = system( cmd_str.data() ); 00466 if( mIndexStream ) 00467 { 00468 cmd_str = "mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(indexLocation())); 00469 rc = system( cmd_str.data() ); 00470 } 00471 break; 00472 00473 case lock_none: 00474 default: 00475 rc = 0; 00476 break; 00477 } 00478 00479 return rc; 00480 } 00481 00482 00483 //----------------------------------------------------------------------------- 00484 KMFolderIndex::IndexStatus KMFolderMbox::indexStatus() 00485 { 00486 QFileInfo contInfo(location()); 00487 QFileInfo indInfo(indexLocation()); 00488 00489 if (!contInfo.exists()) return KMFolderIndex::IndexOk; 00490 if (!indInfo.exists()) return KMFolderIndex::IndexMissing; 00491 00492 // Check whether the mbox file is more than 5 seconds newer than the index 00493 // file. The 5 seconds are added to reduce the number of false alerts due 00494 // to slightly out of sync clocks of the NFS server and the local machine. 00495 return ( contInfo.lastModified() > indInfo.lastModified().addSecs(5) ) 00496 ? KMFolderIndex::IndexTooOld 00497 : KMFolderIndex::IndexOk; 00498 } 00499 00500 00501 //----------------------------------------------------------------------------- 00502 int KMFolderMbox::createIndexFromContents() 00503 { 00504 char line[MAX_LINE]; 00505 char status[8], xstatus[8]; 00506 QCString subjStr, dateStr, fromStr, toStr, xmarkStr, *lastStr=0; 00507 QCString replyToIdStr, replyToAuxIdStr, referencesStr, msgIdStr; 00508 bool atEof = false; 00509 bool inHeader = true; 00510 KMMsgInfo* mi; 00511 QString msgStr; 00512 QRegExp regexp(MSG_SEPERATOR_REGEX); 00513 int i, num, numStatus; 00514 short needStatus; 00515 00516 assert(mStream != 0); 00517 rewind(mStream); 00518 00519 mMsgList.clear(); 00520 00521 num = -1; 00522 numStatus= 11; 00523 off_t offs = 0; 00524 size_t size = 0; 00525 dateStr = ""; 00526 fromStr = ""; 00527 toStr = ""; 00528 subjStr = ""; 00529 *status = '\0'; 00530 *xstatus = '\0'; 00531 xmarkStr = ""; 00532 replyToIdStr = ""; 00533 replyToAuxIdStr = ""; 00534 referencesStr = ""; 00535 msgIdStr = ""; 00536 needStatus = 3; 00537 00538 00539 while (!atEof) 00540 { 00541 off_t pos = ftell(mStream); 00542 if (!fgets(line, MAX_LINE, mStream)) atEof = true; 00543 00544 if (atEof || 00545 (strncmp(line,MSG_SEPERATOR_START, msgSepLen)==0 && 00546 regexp.search(line) >= 0)) 00547 { 00548 size = pos - offs; 00549 pos = ftell(mStream); 00550 00551 if (num >= 0) 00552 { 00553 if (numStatus <= 0) 00554 { 00555 msgStr = i18n("Creating index file: one message done", "Creating index file: %n messages done", num); 00556 emit statusMsg(msgStr); 00557 numStatus = 10; 00558 } 00559 00560 if (size > 0) 00561 { 00562 msgIdStr = msgIdStr.stripWhiteSpace(); 00563 if( !msgIdStr.isEmpty() ) { 00564 int rightAngle; 00565 rightAngle = msgIdStr.find( '>' ); 00566 if( rightAngle != -1 ) 00567 msgIdStr.truncate( rightAngle + 1 ); 00568 } 00569 00570 replyToIdStr = replyToIdStr.stripWhiteSpace(); 00571 if( !replyToIdStr.isEmpty() ) { 00572 int rightAngle; 00573 rightAngle = replyToIdStr.find( '>' ); 00574 if( rightAngle != -1 ) 00575 replyToIdStr.truncate( rightAngle + 1 ); 00576 } 00577 00578 referencesStr = referencesStr.stripWhiteSpace(); 00579 if( !referencesStr.isEmpty() ) { 00580 int leftAngle, rightAngle; 00581 leftAngle = referencesStr.findRev( '<' ); 00582 if( ( leftAngle != -1 ) 00583 && ( replyToIdStr.isEmpty() || ( replyToIdStr[0] != '<' ) ) ) { 00584 // use the last reference, instead of missing In-Reply-To 00585 replyToIdStr = referencesStr.mid( leftAngle ); 00586 } 00587 00588 // find second last reference 00589 leftAngle = referencesStr.findRev( '<', leftAngle - 1 ); 00590 if( leftAngle != -1 ) 00591 referencesStr = referencesStr.mid( leftAngle ); 00592 rightAngle = referencesStr.findRev( '>' ); 00593 if( rightAngle != -1 ) 00594 referencesStr.truncate( rightAngle + 1 ); 00595 00596 // Store the second to last reference in the replyToAuxIdStr 00597 // It is a good candidate for threading the message below if the 00598 // message In-Reply-To points to is not kept in this folder, 00599 // but e.g. in an Outbox 00600 replyToAuxIdStr = referencesStr; 00601 rightAngle = referencesStr.find( '>' ); 00602 if( rightAngle != -1 ) 00603 replyToAuxIdStr.truncate( rightAngle + 1 ); 00604 } 00605 00606 mi = new KMMsgInfo(this); 00607 mi->init( subjStr.stripWhiteSpace(), 00608 fromStr.stripWhiteSpace(), 00609 toStr.stripWhiteSpace(), 00610 0, KMMsgStatusNew, 00611 xmarkStr.stripWhiteSpace(), 00612 replyToIdStr, replyToAuxIdStr, msgIdStr, 00613 KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown, 00614 KMMsgMDNStateUnknown, offs, size ); 00615 mi->setStatus(status, xstatus); 00616 mi->setDate( dateStr.stripWhiteSpace() ); 00617 mi->setDirty(false); 00618 mMsgList.append(mi); 00619 00620 *status = '\0'; 00621 *xstatus = '\0'; 00622 needStatus = 3; 00623 xmarkStr = ""; 00624 replyToIdStr = ""; 00625 replyToAuxIdStr = ""; 00626 referencesStr = ""; 00627 msgIdStr = ""; 00628 dateStr = ""; 00629 fromStr = ""; 00630 subjStr = ""; 00631 } 00632 else num--,numStatus++; 00633 } 00634 00635 offs = ftell(mStream); 00636 num++; 00637 numStatus--; 00638 inHeader = true; 00639 continue; 00640 } 00641 // Is this a long header line? 00642 if (inHeader && (line[0]=='\t' || line[0]==' ')) 00643 { 00644 i = 0; 00645 while (line [i]=='\t' || line [i]==' ') i++; 00646 if (line [i] < ' ' && line [i]>0) inHeader = false; 00647 else if (lastStr) *lastStr += line + i; 00648 } 00649 else lastStr = 0; 00650 00651 if (inHeader && (line [0]=='\n' || line [0]=='\r')) 00652 inHeader = false; 00653 if (!inHeader) continue; 00654 00655 /* -sanders Make all messages read when auto-recreating index */ 00656 /* Reverted, as it breaks reading the sent mail status, for example. 00657 -till */ 00658 if ((needStatus & 1) && strncasecmp(line, "Status:", 7) == 0) 00659 { 00660 for(i=0; i<4 && line[i+8] > ' '; i++) 00661 status[i] = line[i+8]; 00662 status[i] = '\0'; 00663 needStatus &= ~1; 00664 } 00665 else if ((needStatus & 2) && strncasecmp(line, "X-Status:", 9)==0) 00666 { 00667 for(i=0; i<4 && line[i+10] > ' '; i++) 00668 xstatus[i] = line[i+10]; 00669 xstatus[i] = '\0'; 00670 needStatus &= ~2; 00671 } 00672 else if (strncasecmp(line,"X-KMail-Mark:",13)==0) 00673 xmarkStr = QCString(line+13); 00674 else if (strncasecmp(line,"In-Reply-To:",12)==0) { 00675 replyToIdStr = QCString(line+12); 00676 lastStr = &replyToIdStr; 00677 } 00678 else if (strncasecmp(line,"References:",11)==0) { 00679 referencesStr = QCString(line+11); 00680 lastStr = &referencesStr; 00681 } 00682 else if (strncasecmp(line,"Message-Id:",11)==0) { 00683 msgIdStr = QCString(line+11); 00684 lastStr = &msgIdStr; 00685 } 00686 else if (strncasecmp(line,"Date:",5)==0) 00687 { 00688 dateStr = QCString(line+5); 00689 lastStr = &dateStr; 00690 } 00691 else if (strncasecmp(line,"From:", 5)==0) 00692 { 00693 fromStr = QCString(line+5); 00694 lastStr = &fromStr; 00695 } 00696 else if (strncasecmp(line,"To:", 3)==0) 00697 { 00698 toStr = QCString(line+3); 00699 lastStr = &toStr; 00700 } 00701 else if (strncasecmp(line,"Subject:",8)==0) 00702 { 00703 subjStr = QCString(line+8); 00704 lastStr = &subjStr; 00705 } 00706 } 00707 00708 if (mAutoCreateIndex) 00709 { 00710 emit statusMsg(i18n("Writing index file")); 00711 writeIndex(); 00712 } 00713 else mHeaderOffset = 0; 00714 00715 correctUnreadMsgsCount(); 00716 00717 if (kmkernel->outboxFolder() == this && count() > 0) 00718 KMessageBox::queuedMessageBox(0, KMessageBox::Information, 00719 i18n("Your outbox contains messages which were " 00720 "most likely not created by KMail.\nPlease remove them from there, if you " 00721 "don't want KMail to send them.")); 00722 00723 if ( parent() ) 00724 parent()->manager()->invalidateFolder(kmkernel->msgDict(), this); 00725 return 0; 00726 } 00727 00728 00729 //----------------------------------------------------------------------------- 00730 KMMessage* KMFolderMbox::readMsg(int idx) 00731 { 00732 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx]; 00733 00734 assert(mi!=0 && !mi->isMessage()); 00735 assert(mStream != 0); 00736 00737 KMMessage* msg = new KMMessage(*mi); 00738 msg->fromDwString( getDwString( idx ) ); 00739 mMsgList.set(idx,&msg->toMsgBase()); 00740 00741 return msg; 00742 } 00743 00744 00745 #define STRDIM(x) (sizeof(x)/sizeof(*x)-1) 00746 // performs (\n|^)>{n}From_ -> \1>{n-1}From_ conversion 00747 static size_t unescapeFrom( char* str, size_t strLen ) { 00748 if ( !str ) 00749 return 0; 00750 if ( strLen <= STRDIM(">From ") ) 00751 return strLen; 00752 00753 // yes, *d++ = *s++ is a no-op as long as d == s (until after the 00754 // first >From_), but writes are cheap compared to reads and the 00755 // data is already in the cache from the read, so special-casing 00756 // might even be slower... 00757 const char * s = str; 00758 char * d = str; 00759 const char * const e = str + strLen - STRDIM(">From "); 00760 00761 while ( s < e ) { 00762 if ( *s == '\n' && *(s+1) == '>' ) { // we can do the lookahead, since e is 6 chars from the end! 00763 *d++ = *s++; // == '\n' 00764 *d++ = *s++; // == '>' 00765 while ( s < e && *s == '>' ) 00766 *d++ = *s++; 00767 if ( qstrncmp( s, "From ", STRDIM("From ") ) == 0 ) 00768 --d; 00769 } 00770 *d++ = *s++; // yes, s might be e here, but e is not the end :-) 00771 } 00772 // copy the rest: 00773 while ( s < str + strLen ) 00774 *d++ = *s++; 00775 if ( d < s ) // only NUL-terminate if it's shorter 00776 *d = 0; 00777 00778 return d - str; 00779 } 00780 00781 //static 00782 QCString KMFolderMbox::escapeFrom( const QCString & str ) { 00783 const unsigned int strLen = str.length(); 00784 if ( strLen <= STRDIM("From ") ) 00785 return str; 00786 // worst case: \nFrom_\nFrom_\nFrom_... => grows to 7/6 00787 QCString result( int( strLen + 5 ) / 6 * 7 + 1 ); 00788 00789 const char * s = str.data(); 00790 const char * const e = s + strLen - STRDIM("From "); 00791 char * d = result.data(); 00792 00793 bool onlyAnglesAfterLF = false; // dont' match ^From_ 00794 while ( s < e ) { 00795 switch ( *s ) { 00796 case '\n': 00797 onlyAnglesAfterLF = true; 00798 break; 00799 case '>': 00800 break; 00801 case 'F': 00802 if ( onlyAnglesAfterLF && qstrncmp( s+1, "rom ", STRDIM("rom ") ) == 0 ) 00803 *d++ = '>'; 00804 // fall through 00805 default: 00806 onlyAnglesAfterLF = false; 00807 break; 00808 } 00809 *d++ = *s++; 00810 } 00811 while ( s < str.data() + strLen ) 00812 *d++ = *s++; 00813 00814 result.truncate( d - result.data() ); 00815 return result; 00816 } 00817 00818 #undef STRDIM 00819 00820 //----------------------------------------------------------------------------- 00821 QCString& KMFolderMbox::getMsgString(int idx, QCString &mDest) 00822 { 00823 unsigned long msgSize; 00824 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx]; 00825 00826 assert(mi!=0); 00827 assert(mStream != 0); 00828 00829 msgSize = mi->msgSize(); 00830 mDest.resize(msgSize+2); 00831 00832 fseek(mStream, mi->folderOffset(), SEEK_SET); 00833 fread(mDest.data(), msgSize, 1, mStream); 00834 mDest[msgSize] = '\0'; 00835 00836 size_t newMsgSize = unescapeFrom( mDest.data(), msgSize ); 00837 newMsgSize = crlf2lf( mDest.data(), newMsgSize ); 00838 00839 return mDest; 00840 } 00841 00842 00843 //----------------------------------------------------------------------------- 00844 DwString KMFolderMbox::getDwString(int idx) 00845 { 00846 KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx]; 00847 00848 assert(mi!=0); 00849 assert(mStream != 0); 00850 00851 size_t msgSize = mi->msgSize(); 00852 char* msgText = new char[ msgSize + 1 ]; 00853 00854 fseek(mStream, mi->folderOffset(), SEEK_SET); 00855 fread(msgText, msgSize, 1, mStream); 00856 msgText[msgSize] = '\0'; 00857 00858 size_t newMsgSize = unescapeFrom( msgText, msgSize ); 00859 newMsgSize = crlf2lf( msgText, newMsgSize ); 00860 00861 DwString msgStr; 00862 // the DwString takes possession of msgText, so we must not delete msgText 00863 msgStr.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize ); 00864 return msgStr; 00865 } 00866 00867 00868 //----------------------------------------------------------------------------- 00869 int KMFolderMbox::addMsg(KMMessage* aMsg, int* aIndex_ret) 00870 { 00871 if (!canAddMsgNow(aMsg, aIndex_ret)) return 0; 00872 bool opened = false; 00873 QCString msgText; 00874 char endStr[3]; 00875 int idx = -1, rc; 00876 KMFolder* msgParent; 00877 bool editing = false; 00878 int growth = 0; 00879 00880 /* Then we can also disable it completely, this wastes time, at least for IMAP 00881 if (KMFolder::IndexOk != indexStatus()) { 00882 kdDebug(5006) << "Critical error: " << location() << 00883 " has been modified by an external application while KMail was running." << endl; 00884 // exit(1); backed out due to broken nfs 00885 } */ 00886 00887 if (!mStream) 00888 { 00889 opened = true; 00890 rc = open(); 00891 kdDebug(5006) << "addMsg-open: " << rc << endl; 00892 if (rc) return rc; 00893 } 00894 00895 // take message out of the folder it is currently in, if any 00896 msgParent = aMsg->parent(); 00897 if (msgParent) 00898 { 00899 if (msgParent==this) 00900 { 00901 if (kmkernel->folderIsDraftOrOutbox(this)) 00902 //special case for Edit message. 00903 { 00904 kdDebug(5006) << "Editing message in outbox or drafts" << endl; 00905 editing = true; 00906 } 00907 else 00908 return 0; 00909 } 00910 00911 idx = msgParent->find(aMsg); 00912 msgParent->getMsg( idx ); 00913 } 00914 00915 if (folderType() != KMFolderTypeImap) 00916 { 00917 /* 00918 QFile fileD0( "testdat_xx-kmfoldermbox-0" ); 00919 if( fileD0.open( IO_WriteOnly ) ) { 00920 QDataStream ds( &fileD0 ); 00921 ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() ); 00922 fileD0.close(); // If data is 0 we just create a zero length file. 00923 } 00924 */ 00925 aMsg->setStatusFields(); 00926 /* 00927 QFile fileD1( "testdat_xx-kmfoldermbox-1" ); 00928 if( fileD1.open( IO_WriteOnly ) ) { 00929 QDataStream ds( &fileD1 ); 00930 ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() ); 00931 fileD1.close(); // If data is 0 we just create a zero length file. 00932 } 00933 */ 00934 if (aMsg->headerField("Content-Type").isEmpty()) // This might be added by 00935 aMsg->removeHeaderField("Content-Type"); // the line above 00936 } 00937 msgText = escapeFrom( aMsg->asString() ); 00938 size_t len = msgText.length(); 00939 00940 assert(mStream != 0); 00941 clearerr(mStream); 00942 if (len <= 0) 00943 { 00944 kdDebug(5006) << "Message added to folder `" << name() << "' contains no data. Ignoring it." << endl; 00945 if (opened) close(); 00946 return 0; 00947 } 00948 00949 // Make sure the file is large enough to check for an end 00950 // character 00951 fseek(mStream, 0, SEEK_END); 00952 off_t revert = ftell(mStream); 00953 if (ftell(mStream) >= 2) { 00954 // write message to folder file 00955 fseek(mStream, -2, SEEK_END); 00956 fread(endStr, 1, 2, mStream); // ensure separating empty line 00957 if (ftell(mStream) > 0 && endStr[0]!='\n') { 00958 ++growth; 00959 if (endStr[1]!='\n') { 00960 //printf ("****endStr[1]=%c\n", endStr[1]); 00961 fwrite("\n\n", 1, 2, mStream); 00962 ++growth; 00963 } 00964 else fwrite("\n", 1, 1, mStream); 00965 } 00966 } 00967 fseek(mStream,0,SEEK_END); // this is needed on solaris and others 00968 int error = ferror(mStream); 00969 if (error) 00970 { 00971 if (opened) close(); 00972 return error; 00973 } 00974 00975 QCString address( aMsg->fromEmail() ); 00976 if ( address.isEmpty() ) 00977 address = "unknown@unknown.invalid"; 00978 fprintf(mStream, "From %s %s\n", address.data(), 00979 (const char *)aMsg->dateShortStr()); 00980 off_t offs = ftell(mStream); 00981 fwrite(msgText, len, 1, mStream); 00982 if (msgText[(int)len-1]!='\n') fwrite("\n\n", 1, 2, mStream); 00983 fflush(mStream); 00984 size_t size = ftell(mStream) - offs; 00985 00986 error = ferror(mStream); 00987 if (error) { 00988 kdDebug(5006) << "Error: Could not add message to folder: " << strerror(errno) << endl; 00989 if (ftell(mStream) > revert) { 00990 kdDebug(5006) << "Undoing changes" << endl; 00991 truncate( QFile::encodeName(location()), revert ); 00992 } 00993 kmkernel->emergencyExit( i18n("Could not add message to folder: ") + QString::fromLocal8Bit(strerror(errno))); 00994 00995 /* This code is not 100% reliable 00996 bool busy = kmkernel->kbp()->isBusy(); 00997 if (busy) kmkernel->kbp()->idle(); 00998 KMessageBox::sorry(0, 00999 i18n("Unable to add message to folder.\n" 01000 "(No space left on device or insufficient quota?)\n" 01001 "Free space and sufficient quota are required to continue safely.")); 01002 if (busy) kmkernel->kbp()->busy(); 01003 if (opened) close(); 01004 kmkernel->kbp()->idle(); 01005 */ 01006 return error; 01007 } 01008 01009 if (msgParent) { 01010 if (idx >= 0) msgParent->take(idx); 01011 } 01012 // if (mAccount) aMsg->removeHeaderField("X-UID"); 01013 01014 if (aMsg->isUnread() || aMsg->isNew() || 01015 (this == kmkernel->outboxFolder())) { 01016 if (mUnreadMsgs == -1) mUnreadMsgs = 1; 01017 else ++mUnreadMsgs; 01018 emit numUnreadMsgsChanged( this ); 01019 } 01020 ++mTotalMsgs; 01021 01022 // store information about the position in the folder file in the message 01023 aMsg->setParent(this); 01024 aMsg->setFolderOffset(offs); 01025 aMsg->setMsgSize(size); 01026 idx = mMsgList.append(&aMsg->toMsgBase()); 01027 if (aMsg->getMsgSerNum() <= 0) 01028 aMsg->setMsgSerNum(); 01029 01030 // change the length of the previous message to encompass white space added 01031 if ((idx > 0) && (growth > 0)) { 01032 // don't grow if a deleted message claims space at the end of the file 01033 if ((ulong)revert == mMsgList[idx - 1]->folderOffset() + mMsgList[idx - 1]->msgSize() ) 01034 mMsgList[idx - 1]->setMsgSize( mMsgList[idx - 1]->msgSize() + growth ); 01035 } 01036 01037 // write index entry if desired 01038 if (mAutoCreateIndex) 01039 { 01040 assert(mIndexStream != 0); 01041 clearerr(mIndexStream); 01042 fseek(mIndexStream, 0, SEEK_END); 01043 revert = ftell(mIndexStream); 01044 01045 KMMsgBase * mb = &aMsg->toMsgBase(); 01046 int len; 01047 const uchar *buffer = mb->asIndexString(len); 01048 fwrite(&len,sizeof(len), 1, mIndexStream); 01049 mb->setIndexOffset( ftell(mIndexStream) ); 01050 mb->setIndexLength( len ); 01051 if(fwrite(buffer, len, 1, mIndexStream) != 1) 01052 kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl; 01053 01054 fflush(mIndexStream); 01055 error = ferror(mIndexStream); 01056 01057 error |= appendtoMsgDict(idx); 01058 01059 if (error) { 01060 kdWarning(5006) << "Error: Could not add message to folder (No space left on device?)" << endl; 01061 if (ftell(mIndexStream) > revert) { 01062 kdWarning(5006) << "Undoing changes" << endl; 01063 truncate( QFile::encodeName(indexLocation()), revert ); 01064 } 01065 if ( errno ) 01066 kmkernel->emergencyExit( i18n("Could not add message to folder:") + QString::fromLocal8Bit(strerror(errno))); 01067 else 01068 kmkernel->emergencyExit( i18n("Could not add message to folder (No space left on device?)") ); 01069 01070 /* This code may not be 100% reliable 01071 bool busy = kmkernel->kbp()->isBusy(); 01072 if (busy) kmkernel->kbp()->idle(); 01073 KMessageBox::sorry(0, 01074 i18n("Unable to add message to folder.\n" 01075 "(No space left on device or insufficient quota?)\n" 01076 "Free space and sufficient quota are required to continue safely.")); 01077 if (busy) kmkernel->kbp()->busy(); 01078 if (opened) close(); 01079 */ 01080 return error; 01081 } 01082 } 01083 01084 // some "paper work" 01085 if (aIndex_ret) *aIndex_ret = idx; 01086 emitMsgAddedSignals(idx); 01087 if (opened) close(); 01088 01089 // All streams have been flushed without errors if we arrive here 01090 // Return success! 01091 // (Don't return status of stream, it may have been closed already.) 01092 return 0; 01093 } 01094 01095 01096 //----------------------------------------------------------------------------- 01097 int KMFolderMbox::compact() 01098 { 01099 QString tempName; 01100 QString msgStr; 01101 int rc = 0; 01102 int openCount = mOpenCount; 01103 01104 if (!needsCompact) 01105 return 0; 01106 01107 if (!mCompactable) { 01108 kdDebug(5006) << location() << " compaction skipped." << endl; 01109 return 0; 01110 } 01111 kdDebug(5006) << "Compacting " << idString() << endl; 01112 01113 if (KMFolderIndex::IndexOk != indexStatus()) { 01114 kdDebug(5006) << "Critical error: " << location() << 01115 " has been modified by an external application while KMail was running." << endl; 01116 // exit(1); backed out due to broken nfs 01117 } 01118 01119 tempName = path() + "/." + name() + ".compacted"; 01120 mode_t old_umask = umask(077); 01121 FILE *tmpfile = fopen(QFile::encodeName(tempName), "w"); 01122 umask(old_umask); 01123 if (!tmpfile) 01124 return errno; 01125 open(); 01126 01127 KMMsgInfo* mi; 01128 size_t msize; 01129 off_t folder_offset; 01130 off_t offs=0; 01131 int msgs=0; 01132 QCString mtext; 01133 for(unsigned int idx = 0; idx < mMsgList.count(); idx++) { 01134 if(!(msgs++ % 10)) { 01135 msgStr = i18n("Compacting folder: one message done", 01136 "Compacting folder: %n messages done", msgs); 01137 if (!kmkernel->shuttingDown()) 01138 emit statusMsg(msgStr); 01139 } 01140 mi = (KMMsgInfo*)mMsgList.at(idx); 01141 msize = mi->msgSize(); 01142 if (mtext.size() < msize + 2) 01143 mtext.resize(msize+2); 01144 folder_offset = mi->folderOffset(); 01145 01146 //now we need to find the separator! grr... 01147 for(off_t i = folder_offset-25; true; i -= 20) { 01148 off_t chunk_offset = i <= 0 ? 0 : i; 01149 if(fseek(mStream, chunk_offset, SEEK_SET) == -1) { 01150 rc = errno; 01151 break; 01152 } 01153 if (mtext.size() < 20) 01154 mtext.resize(20); 01155 fread(mtext.data(), 20, 1, mStream); 01156 if(i <= 0) { //woops we've reached the top of the file, last try.. 01157 if ( mtext.contains( "from ", false ) ) { 01158 if (mtext.size() < (size_t)folder_offset) 01159 mtext.resize(folder_offset); 01160 if(fseek(mStream, chunk_offset, SEEK_SET) == -1 || 01161 !fread(mtext.data(), folder_offset, 1, mStream) || 01162 !fwrite(mtext.data(), folder_offset, 1, tmpfile)) { 01163 rc = errno; 01164 break; 01165 } 01166 offs += folder_offset; 01167 } else { 01168 rc = 666; 01169 } 01170 break; 01171 } else { 01172 int last_crlf = -1; 01173 for(int i2 = 0; i2 < 20; i2++) { 01174 if(*(mtext.data()+i2) == '\n') 01175 last_crlf = i2; 01176 } 01177 if(last_crlf != -1) { 01178 int size = folder_offset - (i + last_crlf+1); 01179 if ((int)mtext.size() < size) 01180 mtext.resize(size); 01181 if(fseek(mStream, i + last_crlf+1, SEEK_SET) == -1 || 01182 !fread(mtext.data(), size, 1, mStream) || 01183 !fwrite(mtext.data(), size, 1, tmpfile)) { 01184 rc = errno; 01185 break; 01186 } 01187 offs += size; 01188 break; 01189 } 01190 } 01191 } 01192 if (rc) 01193 break; 01194 01195 //now actually write the message 01196 if(fseek(mStream, folder_offset, SEEK_SET) == -1 || 01197 !fread(mtext.data(), msize, 1, mStream) || !fwrite(mtext.data(), msize, 1, tmpfile)) { 01198 rc = errno; 01199 break; 01200 } 01201 mi->setFolderOffset(offs); 01202 offs += msize; 01203 } 01204 if (!rc) 01205 rc = fflush(tmpfile); 01206 if (!rc) 01207 rc = fsync(fileno(tmpfile)); 01208 rc |= fclose(tmpfile); 01209 if (!rc) { 01210 bool autoCreate = mAutoCreateIndex; 01211 QFileInfo inf(location()); 01212 QString box; 01213 if (inf.isSymLink()) 01214 box = inf.readLink(); 01215 if (!box) 01216 box = location(); 01217 ::rename(QFile::encodeName(tempName), QFile::encodeName(box)); 01218 writeIndex(); 01219 writeConfig(); 01220 mAutoCreateIndex = false; 01221 close(true); 01222 mAutoCreateIndex = autoCreate; 01223 needsCompact = false; // We are clean now 01224 } 01225 else 01226 { 01227 close(); 01228 kdDebug(5006) << "Error occurred while compacting" << endl; 01229 kdDebug(5006) << location() << endl; 01230 kdDebug(5006) << "Compaction aborted." << endl; 01231 } 01232 01233 if (openCount > 0) 01234 { 01235 open(); 01236 mOpenCount = openCount; 01237 } 01238 emit changed(); 01239 return 0; 01240 01241 } 01242 01243 01244 //----------------------------------------------------------------------------- 01245 void KMFolderMbox::setLockType( LockType ltype ) 01246 { 01247 mLockType = ltype; 01248 } 01249 01250 //----------------------------------------------------------------------------- 01251 void KMFolderMbox::setProcmailLockFileName( const QString &fname ) 01252 { 01253 mProcmailLockFileName = fname; 01254 } 01255 01256 //----------------------------------------------------------------------------- 01257 int KMFolderMbox::removeContents() 01258 { 01259 int rc = 0; 01260 rc = unlink(QFile::encodeName(location())); 01261 return rc; 01262 } 01263 01264 //----------------------------------------------------------------------------- 01265 int KMFolderMbox::expungeContents() 01266 { 01267 int rc = 0; 01268 if (truncate(QFile::encodeName(location()), 0)) 01269 rc = errno; 01270 return rc; 01271 } 01272 01273 //----------------------------------------------------------------------------- 01274 #include "kmfoldermbox.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