kmail

kmfoldermbox.cpp
1 /*
2  * kmail: KDE mail client
3  * Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 #include <config.h>
21 #include <tqfileinfo.h>
22 #include <tqregexp.h>
23 
24 #include "kmfoldermbox.h"
25 #include "folderstorage.h"
26 #include "kmfolder.h"
27 #include "kmkernel.h"
28 #include "kmmsgdict.h"
29 #include "undostack.h"
30 #include "kcursorsaver.h"
31 #include "jobscheduler.h"
32 #include "compactionjob.h"
33 #include "util.h"
34 
35 #include <kdebug.h>
36 #include <tdelocale.h>
37 #include <tdemessagebox.h>
38 #include <knotifyclient.h>
39 #include <kprocess.h>
40 #include <tdeconfig.h>
41 
42 #include <ctype.h>
43 #include <stdio.h>
44 #include <errno.h>
45 #include <assert.h>
46 #include <ctype.h>
47 #include <unistd.h>
48 
49 #include <fcntl.h>
50 
51 #include <stdlib.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/file.h>
55 #include "broadcaststatus.h"
56 using KPIM::BroadcastStatus;
57 
58 #ifndef MAX_LINE
59 #define MAX_LINE 4096
60 #endif
61 #ifndef INIT_MSGS
62 #define INIT_MSGS 8
63 #endif
64 
65 // Regular expression to find the line that seperates messages in a mail
66 // folder:
67 #define MSG_SEPERATOR_START "From "
68 #define MSG_SEPERATOR_START_LEN (sizeof(MSG_SEPERATOR_START) - 1)
69 #define MSG_SEPERATOR_REGEX "^From .*[0-9][0-9]:[0-9][0-9]"
70 
71 
72 //-----------------------------------------------------------------------------
73 KMFolderMbox::KMFolderMbox(KMFolder* folder, const char* name)
74  : KMFolderIndex(folder, name)
75 {
76  mStream = 0;
77  mFilesLocked = false;
78  mReadOnly = false;
79  mLockType = lock_none;
80 }
81 
82 
83 //-----------------------------------------------------------------------------
84 KMFolderMbox::~KMFolderMbox()
85 {
86  if (mOpenCount>0)
87  close("~kmfoldermbox", true);
88  if (kmkernel->undoStack())
89  kmkernel->undoStack()->folderDestroyed( folder() );
90 }
91 
92 //-----------------------------------------------------------------------------
93 int KMFolderMbox::open(const char *owner)
94 {
95  Q_UNUSED( owner );
96  int rc = 0;
97 
98  mOpenCount++;
99  kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
100 
101  if (mOpenCount > 1) return 0; // already open
102 
103  assert(!folder()->name().isEmpty());
104 
105  mFilesLocked = false;
106  mStream = fopen(TQFile::encodeName(location()), "r+"); // messages file
107  if (!mStream)
108  {
109  KNotifyClient::event( 0, "warning",
110  i18n("Cannot open file \"%1\":\n%2").arg(location()).arg(strerror(errno)));
111  kdDebug(5006) << "Cannot open folder `" << location() << "': " << strerror(errno) << endl;
112  mOpenCount = 0;
113  return errno;
114  }
115 
116  lock();
117 
118  if (!folder()->path().isEmpty())
119  {
120  KMFolderIndex::IndexStatus index_status = indexStatus();
121  // test if index file exists and is up-to-date
122  if (KMFolderIndex::IndexOk != index_status)
123  {
124  // only show a warning if the index file exists, otherwise it can be
125  // silently regenerated
126  if (KMFolderIndex::IndexTooOld == index_status) {
127  TQString msg = i18n("<qt><p>The index of folder '%2' seems "
128  "to be out of date. To prevent message "
129  "corruption the index will be "
130  "regenerated. As a result deleted "
131  "messages might reappear and status "
132  "flags might be lost.</p>"
133  "<p>Please read the corresponding entry "
134  "in the <a href=\"%1\">FAQ section of the manual "
135  "of KMail</a> for "
136  "information about how to prevent this "
137  "problem from happening again.</p></qt>")
138  .arg("help:/kmail/faq.html#faq-index-regeneration")
139  .arg(name());
140  // When KMail is starting up we have to show a non-blocking message
141  // box so that the initialization can continue. We don't show a
142  // queued message box when KMail isn't starting up because queued
143  // message boxes don't have a "Don't ask again" checkbox.
144  if (kmkernel->startingUp())
145  {
146  TDEConfigGroup configGroup( KMKernel::config(), "Notification Messages" );
147  bool showMessage =
148  configGroup.readBoolEntry( "showIndexRegenerationMessage", true );
149  if (showMessage)
150  KMessageBox::queuedMessageBox( 0, KMessageBox::Information,
151  msg, i18n("Index Out of Date"),
152  KMessageBox::AllowLink );
153  }
154  else
155  {
156  KCursorSaver idle(KBusyPtr::idle());
157  KMessageBox::information( 0, msg, i18n("Index Out of Date"),
158  "showIndexRegenerationMessage",
159  KMessageBox::AllowLink );
160  }
161  }
162  TQString str;
163  mIndexStream = 0;
164  str = i18n("Folder `%1' changed. Recreating index.")
165  .arg(name());
166  emit statusMsg(str);
167  } else {
168  mIndexStream = fopen(TQFile::encodeName(indexLocation()), "r+"); // index file
169  if ( mIndexStream ) {
170  fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
171  updateIndexStreamPtr();
172  }
173  }
174 
175  if (!mIndexStream)
177  else
178  if (!readIndex())
180  }
181  else
182  {
183  mAutoCreateIndex = false;
185  }
186 
187  mChanged = false;
188 
189  fcntl(fileno(mStream), F_SETFD, FD_CLOEXEC);
190  if (mIndexStream)
191  fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
192 
193  return rc;
194 }
195 
196 //----------------------------------------------------------------------------
197 int KMFolderMbox::canAccess()
198 {
199  assert(!folder()->name().isEmpty());
200 
201  if (access(TQFile::encodeName(location()), R_OK | W_OK) != 0) {
202  kdDebug(5006) << "KMFolderMbox::access call to access function failed" << endl;
203  return 1;
204  }
205  return 0;
206 }
207 
208 //-----------------------------------------------------------------------------
209 int KMFolderMbox::create()
210 {
211  int rc;
212  int old_umask;
213 
214  assert(!folder()->name().isEmpty());
215  assert(mOpenCount == 0);
216 
217  kdDebug(5006) << "Creating folder " << name() << endl;
218  if (access(TQFile::encodeName(location()), F_OK) == 0) {
219  kdDebug(5006) << "KMFolderMbox::create call to access function failed." << endl;
220  kdDebug(5006) << "File:: " << endl;
221  kdDebug(5006) << "Error " << endl;
222  return EEXIST;
223  }
224 
225  old_umask = umask(077);
226  mStream = fopen(TQFile::encodeName(location()), "w+"); //sven; open RW
227  umask(old_umask);
228 
229  if (!mStream) return errno;
230 
231  fcntl(fileno(mStream), F_SETFD, FD_CLOEXEC);
232 
233  if (!folder()->path().isEmpty())
234  {
235  old_umask = umask(077);
236  mIndexStream = fopen(TQFile::encodeName(indexLocation()), "w+"); //sven; open RW
237  updateIndexStreamPtr(true);
238  umask(old_umask);
239 
240  if (!mIndexStream) return errno;
241  fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
242  }
243  else
244  {
245  mAutoCreateIndex = false;
246  }
247 
248  mOpenCount++;
249  mChanged = false;
250 
251  rc = writeIndex();
252  if (!rc) lock();
253  return rc;
254 }
255 
256 
257 //-----------------------------------------------------------------------------
258 void KMFolderMbox::reallyDoClose(const char* owner)
259 {
260  Q_UNUSED( owner );
261  if (mAutoCreateIndex)
262  {
263  if (KMFolderIndex::IndexOk != indexStatus()) {
264  kdDebug(5006) << "Critical error: " << location() <<
265  " has been modified by an external application while KMail was running." << endl;
266  // exit(1); backed out due to broken nfs
267  }
268 
269  updateIndex();
270  writeConfig();
271  }
272 
273  if (!noContent()) {
274  if (mStream) unlock();
275  mMsgList.clear(true);
276 
277  if (mStream) fclose(mStream);
278  if (mIndexStream) {
279  fclose(mIndexStream);
280  updateIndexStreamPtr(true);
281  }
282  }
283  mOpenCount = 0;
284  mStream = 0;
285  mIndexStream = 0;
286  mFilesLocked = false;
287  mUnreadMsgs = -1;
288 
289  mMsgList.reset(INIT_MSGS);
290 }
291 
292 //-----------------------------------------------------------------------------
293 void KMFolderMbox::sync()
294 {
295  if (mOpenCount > 0)
296  if (!mStream || fsync(fileno(mStream)) ||
297  !mIndexStream || fsync(fileno(mIndexStream))) {
298  kmkernel->emergencyExit( i18n("Could not sync index file <b>%1</b>: %2").arg( indexLocation() ).arg(errno ? TQString::fromLocal8Bit(strerror(errno)) : i18n("Internal error. Please copy down the details and report a bug.")));
299  }
300 }
301 
302 //-----------------------------------------------------------------------------
303 int KMFolderMbox::lock()
304 {
305  int rc;
306  struct flock fl;
307  fl.l_type=F_WRLCK;
308  fl.l_whence=0;
309  fl.l_start=0;
310  fl.l_len=0;
311  fl.l_pid=-1;
312  TQCString cmd_str;
313  assert(mStream != 0);
314  mFilesLocked = false;
315  mReadOnly = false;
316 
317  switch( mLockType )
318  {
319  case FCNTL:
320  rc = fcntl(fileno(mStream), F_SETLKW, &fl);
321 
322  if (rc < 0)
323  {
324  kdDebug(5006) << "Cannot lock folder `" << location() << "': "
325  << strerror(errno) << " (" << errno << ")" << endl;
326  mReadOnly = true;
327  return errno;
328  }
329 
330  if (mIndexStream)
331  {
332  rc = fcntl(fileno(mIndexStream), F_SETLK, &fl);
333 
334  if (rc < 0)
335  {
336  kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
337  << strerror(errno) << " (" << errno << ")" << endl;
338  rc = errno;
339  fl.l_type = F_UNLCK;
340  /*rc =*/ fcntl(fileno(mIndexStream), F_SETLK, &fl);
341  mReadOnly = true;
342  return rc;
343  }
344  }
345  break;
346 
347  case procmail_lockfile:
348  cmd_str = "lockfile -l20 -r5 ";
349  if (!mProcmailLockFileName.isEmpty())
350  cmd_str += TQFile::encodeName(TDEProcess::quote(mProcmailLockFileName));
351  else
352  cmd_str += TQFile::encodeName(TDEProcess::quote(location() + ".lock"));
353 
354  rc = system( cmd_str.data() );
355  if( rc != 0 )
356  {
357  kdDebug(5006) << "Cannot lock folder `" << location() << "': "
358  << strerror(rc) << " (" << rc << ")" << endl;
359  mReadOnly = true;
360  return rc;
361  }
362  if( mIndexStream )
363  {
364  cmd_str = "lockfile -l20 -r5 " + TQFile::encodeName(TDEProcess::quote(indexLocation() + ".lock"));
365  rc = system( cmd_str.data() );
366  if( rc != 0 )
367  {
368  kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
369  << strerror(rc) << " (" << rc << ")" << endl;
370  mReadOnly = true;
371  return rc;
372  }
373  }
374  break;
375 
376  case mutt_dotlock:
377  cmd_str = "mutt_dotlock " + TQFile::encodeName(TDEProcess::quote(location()));
378  rc = system( cmd_str.data() );
379  if( rc != 0 )
380  {
381  kdDebug(5006) << "Cannot lock folder `" << location() << "': "
382  << strerror(rc) << " (" << rc << ")" << endl;
383  mReadOnly = true;
384  return rc;
385  }
386  if( mIndexStream )
387  {
388  cmd_str = "mutt_dotlock " + TQFile::encodeName(TDEProcess::quote(indexLocation()));
389  rc = system( cmd_str.data() );
390  if( rc != 0 )
391  {
392  kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
393  << strerror(rc) << " (" << rc << ")" << endl;
394  mReadOnly = true;
395  return rc;
396  }
397  }
398  break;
399 
400  case mutt_dotlock_privileged:
401  cmd_str = "mutt_dotlock -p " + TQFile::encodeName(TDEProcess::quote(location()));
402  rc = system( cmd_str.data() );
403  if( rc != 0 )
404  {
405  kdDebug(5006) << "Cannot lock folder `" << location() << "': "
406  << strerror(rc) << " (" << rc << ")" << endl;
407  mReadOnly = true;
408  return rc;
409  }
410  if( mIndexStream )
411  {
412  cmd_str = "mutt_dotlock -p " + TQFile::encodeName(TDEProcess::quote(indexLocation()));
413  rc = system( cmd_str.data() );
414  if( rc != 0 )
415  {
416  kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
417  << strerror(rc) << " (" << rc << ")" << endl;
418  mReadOnly = true;
419  return rc;
420  }
421  }
422  break;
423 
424  case lock_none:
425  default:
426  break;
427  }
428 
429 
430  mFilesLocked = true;
431  return 0;
432 }
433 
434 //-------------------------------------------------------------
435 FolderJob*
436 KMFolderMbox::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
437  KMFolder *folder, TQString, const AttachmentStrategy* ) const
438 {
439  MboxJob *job = new MboxJob( msg, jt, folder );
440  job->setParent( this );
441  return job;
442 }
443 
444 //-------------------------------------------------------------
445 FolderJob*
446 KMFolderMbox::doCreateJob( TQPtrList<KMMessage>& msgList, const TQString& sets,
447  FolderJob::JobType jt, KMFolder *folder ) const
448 {
449  MboxJob *job = new MboxJob( msgList, sets, jt, folder );
450  job->setParent( this );
451  return job;
452 }
453 
454 //-----------------------------------------------------------------------------
455 int KMFolderMbox::unlock()
456 {
457  int rc;
458  struct flock fl;
459  fl.l_type=F_UNLCK;
460  fl.l_whence=0;
461  fl.l_start=0;
462  fl.l_len=0;
463  TQCString cmd_str;
464 
465  assert(mStream != 0);
466  mFilesLocked = false;
467 
468  switch( mLockType )
469  {
470  case FCNTL:
471  if (mIndexStream) fcntl(fileno(mIndexStream), F_SETLK, &fl);
472  fcntl(fileno(mStream), F_SETLK, &fl);
473  rc = errno;
474  break;
475 
476  case procmail_lockfile:
477  cmd_str = "rm -f ";
478  if (!mProcmailLockFileName.isEmpty())
479  cmd_str += TQFile::encodeName(TDEProcess::quote(mProcmailLockFileName));
480  else
481  cmd_str += TQFile::encodeName(TDEProcess::quote(location() + ".lock"));
482 
483  rc = system( cmd_str.data() );
484  if( mIndexStream )
485  {
486  cmd_str = "rm -f " + TQFile::encodeName(TDEProcess::quote(indexLocation() + ".lock"));
487  rc = system( cmd_str.data() );
488  }
489  break;
490 
491  case mutt_dotlock:
492  cmd_str = "mutt_dotlock -u " + TQFile::encodeName(TDEProcess::quote(location()));
493  rc = system( cmd_str.data() );
494  if( mIndexStream )
495  {
496  cmd_str = "mutt_dotlock -u " + TQFile::encodeName(TDEProcess::quote(indexLocation()));
497  rc = system( cmd_str.data() );
498  }
499  break;
500 
501  case mutt_dotlock_privileged:
502  cmd_str = "mutt_dotlock -p -u " + TQFile::encodeName(TDEProcess::quote(location()));
503  rc = system( cmd_str.data() );
504  if( mIndexStream )
505  {
506  cmd_str = "mutt_dotlock -p -u " + TQFile::encodeName(TDEProcess::quote(indexLocation()));
507  rc = system( cmd_str.data() );
508  }
509  break;
510 
511  case lock_none:
512  default:
513  rc = 0;
514  break;
515  }
516 
517  return rc;
518 }
519 
520 
521 //-----------------------------------------------------------------------------
522 KMFolderIndex::IndexStatus KMFolderMbox::indexStatus()
523 {
524  if ( !mCompactable )
525  return KMFolderIndex::IndexCorrupt;
526 
527  TQFileInfo contInfo(location());
528  TQFileInfo indInfo(indexLocation());
529 
530  if (!contInfo.exists()) return KMFolderIndex::IndexOk;
531  if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
532 
533  // Check whether the mbox file is more than 5 seconds newer than the index
534  // file. The 5 seconds are added to reduce the number of false alerts due
535  // to slightly out of sync clocks of the NFS server and the local machine.
536  return ( contInfo.lastModified() > indInfo.lastModified().addSecs(5) )
537  ? KMFolderIndex::IndexTooOld
538  : KMFolderIndex::IndexOk;
539 }
540 
541 
542 //-----------------------------------------------------------------------------
543 int KMFolderMbox::createIndexFromContents()
544 {
545  char line[MAX_LINE];
546  char status[8], xstatus[8];
547  TQCString subjStr, dateStr, fromStr, toStr, xmarkStr, *lastStr=0;
548  TQCString replyToIdStr, replyToAuxIdStr, referencesStr, msgIdStr;
549  TQCString sizeServerStr, uidStr;
550  TQCString contentTypeStr, charset;
551  bool atEof = false;
552  bool inHeader = true;
553  KMMsgInfo* mi;
554  TQString msgStr;
555  TQRegExp regexp(MSG_SEPERATOR_REGEX);
556  int i, num, numStatus;
557  short needStatus;
558 
559  assert(mStream != 0);
560  rewind(mStream);
561 
562  mMsgList.clear();
563 
564  num = -1;
565  numStatus= 11;
566  off_t offs = 0;
567  size_t size = 0;
568  dateStr = "";
569  fromStr = "";
570  toStr = "";
571  subjStr = "";
572  *status = '\0';
573  *xstatus = '\0';
574  xmarkStr = "";
575  replyToIdStr = "";
576  replyToAuxIdStr = "";
577  referencesStr = "";
578  msgIdStr = "";
579  needStatus = 3;
580  size_t sizeServer = 0;
581  ulong uid = 0;
582 
583 
584  while (!atEof)
585  {
586  off_t pos = ftell(mStream);
587  if (!fgets(line, MAX_LINE, mStream)) atEof = true;
588 
589  if (atEof ||
590  (memcmp(line, MSG_SEPERATOR_START, MSG_SEPERATOR_START_LEN)==0 &&
591  regexp.search(line) >= 0))
592  {
593  size = pos - offs;
594  pos = ftell(mStream);
595 
596  if (num >= 0)
597  {
598  if (numStatus <= 0)
599  {
600  msgStr = i18n("Creating index file: one message done", "Creating index file: %n messages done", num);
601  emit statusMsg(msgStr);
602  numStatus = 10;
603  }
604 
605  if (size > 0)
606  {
607  msgIdStr = msgIdStr.stripWhiteSpace();
608  if( !msgIdStr.isEmpty() ) {
609  int rightAngle;
610  rightAngle = msgIdStr.find( '>' );
611  if( rightAngle != -1 )
612  msgIdStr.truncate( rightAngle + 1 );
613  }
614 
615  replyToIdStr = replyToIdStr.stripWhiteSpace();
616  if( !replyToIdStr.isEmpty() ) {
617  int rightAngle;
618  rightAngle = replyToIdStr.find( '>' );
619  if( rightAngle != -1 )
620  replyToIdStr.truncate( rightAngle + 1 );
621  }
622 
623  referencesStr = referencesStr.stripWhiteSpace();
624  if( !referencesStr.isEmpty() ) {
625  int leftAngle, rightAngle;
626  leftAngle = referencesStr.findRev( '<' );
627  if( ( leftAngle != -1 )
628  && ( replyToIdStr.isEmpty() || ( replyToIdStr[0] != '<' ) ) ) {
629  // use the last reference, instead of missing In-Reply-To
630  replyToIdStr = referencesStr.mid( leftAngle );
631  }
632 
633  // find second last reference
634  leftAngle = referencesStr.findRev( '<', leftAngle - 1 );
635  if( leftAngle != -1 )
636  referencesStr = referencesStr.mid( leftAngle );
637  rightAngle = referencesStr.findRev( '>' );
638  if( rightAngle != -1 )
639  referencesStr.truncate( rightAngle + 1 );
640 
641  // Store the second to last reference in the replyToAuxIdStr
642  // It is a good candidate for threading the message below if the
643  // message In-Reply-To points to is not kept in this folder,
644  // but e.g. in an Outbox
645  replyToAuxIdStr = referencesStr;
646  rightAngle = referencesStr.find( '>' );
647  if( rightAngle != -1 )
648  replyToAuxIdStr.truncate( rightAngle + 1 );
649  }
650 
651  contentTypeStr = contentTypeStr.stripWhiteSpace();
652  charset = "";
653  if ( !contentTypeStr.isEmpty() )
654  {
655  int cidx = contentTypeStr.find( "charset=" );
656  if ( cidx != -1 ) {
657  charset = contentTypeStr.mid( cidx + 8 );
658  if ( !charset.isEmpty() && ( charset[0] == '"' ) ) {
659  charset = charset.mid( 1 );
660  }
661  cidx = 0;
662  while ( (unsigned int) cidx < charset.length() ) {
663  if ( charset[cidx] == '"' || ( !isalnum(charset[cidx]) &&
664  charset[cidx] != '-' && charset[cidx] != '_' ) )
665  break;
666  ++cidx;
667  }
668  charset.truncate( cidx );
669  // kdDebug() << "KMFolderMaildir::readFileHeaderIntern() charset found: " <<
670  // charset << " from " << contentTypeStr << endl;
671  }
672  }
673 
674  mi = new KMMsgInfo(folder());
675  mi->init( subjStr.stripWhiteSpace(),
676  fromStr.stripWhiteSpace(),
677  toStr.stripWhiteSpace(),
678  0, KMMsgStatusNew,
679  xmarkStr.stripWhiteSpace(),
680  replyToIdStr, replyToAuxIdStr, msgIdStr,
681  KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown,
682  KMMsgMDNStateUnknown, charset, offs, size, sizeServer, uid );
683  mi->setStatus(status, xstatus);
684  mi->setDate( dateStr.stripWhiteSpace().data() );
685  mi->setDirty(false);
687 
688  *status = '\0';
689  *xstatus = '\0';
690  needStatus = 3;
691  xmarkStr = "";
692  replyToIdStr = "";
693  replyToAuxIdStr = "";
694  referencesStr = "";
695  msgIdStr = "";
696  dateStr = "";
697  fromStr = "";
698  subjStr = "";
699  sizeServer = 0;
700  uid = 0;
701  }
702  else num--,numStatus++;
703  }
704 
705  offs = ftell(mStream);
706  num++;
707  numStatus--;
708  inHeader = true;
709  continue;
710  }
711  // Is this a long header line?
712  if (inHeader && (line[0]=='\t' || line[0]==' '))
713  {
714  i = 0;
715  while (line [i]=='\t' || line [i]==' ') i++;
716  if (line [i] < ' ' && line [i]>0) inHeader = false;
717  else if (lastStr) *lastStr += line + i;
718  }
719  else lastStr = 0;
720 
721  if (inHeader && (line [0]=='\n' || line [0]=='\r'))
722  inHeader = false;
723  if (!inHeader) continue;
724 
725  /* -sanders Make all messages read when auto-recreating index */
726  /* Reverted, as it breaks reading the sent mail status, for example.
727  -till */
728  if ((needStatus & 1) && strncasecmp(line, "Status:", 7) == 0)
729  {
730  for(i=0; i<4 && line[i+8] > ' '; i++)
731  status[i] = line[i+8];
732  status[i] = '\0';
733  needStatus &= ~1;
734  }
735  else if ((needStatus & 2) && strncasecmp(line, "X-Status:", 9)==0)
736  {
737  for(i=0; i<4 && line[i+10] > ' '; i++)
738  xstatus[i] = line[i+10];
739  xstatus[i] = '\0';
740  needStatus &= ~2;
741  }
742  else if (strncasecmp(line,"X-KMail-Mark:",13)==0)
743  xmarkStr = TQCString(line+13);
744  else if (strncasecmp(line,"In-Reply-To:",12)==0) {
745  replyToIdStr = TQCString(line+12);
746  lastStr = &replyToIdStr;
747  }
748  else if (strncasecmp(line,"References:",11)==0) {
749  referencesStr = TQCString(line+11);
750  lastStr = &referencesStr;
751  }
752  else if (strncasecmp(line,"Message-Id:",11)==0) {
753  msgIdStr = TQCString(line+11);
754  lastStr = &msgIdStr;
755  }
756  else if (strncasecmp(line,"Date:",5)==0)
757  {
758  dateStr = TQCString(line+5);
759  lastStr = &dateStr;
760  }
761  else if (strncasecmp(line,"From:", 5)==0)
762  {
763  fromStr = TQCString(line+5);
764  lastStr = &fromStr;
765  }
766  else if (strncasecmp(line,"To:", 3)==0)
767  {
768  toStr = TQCString(line+3);
769  lastStr = &toStr;
770  }
771  else if (strncasecmp(line,"Subject:",8)==0)
772  {
773  subjStr = TQCString(line+8);
774  lastStr = &subjStr;
775  }
776  else if (strncasecmp(line,"X-Length:",9)==0)
777  {
778  sizeServerStr = TQCString(line+9);
779  sizeServer = sizeServerStr.toULong();
780  lastStr = &sizeServerStr;
781  }
782  else if (strncasecmp(line,"X-UID:",6)==0)
783  {
784  uidStr = TQCString(line+6);
785  uid = uidStr.toULong();
786  lastStr = &uidStr;
787  }
788  else if (strncasecmp(line, "Content-Type:", 13) == 0)
789  {
790  contentTypeStr = TQCString(line+13);
791  lastStr = &contentTypeStr;
792  }
793  }
794 
795  if (mAutoCreateIndex)
796  {
797  emit statusMsg(i18n("Writing index file"));
798  writeIndex();
799  }
800  else mHeaderOffset = 0;
801 
803 
804  if (kmkernel->outboxFolder() == folder() && count() > 0)
805  KMessageBox::queuedMessageBox(0, KMessageBox::Information,
806  i18n("Your outbox contains messages which were "
807  "most-likely not created by KMail;\nplease remove them from there if you "
808  "do not want KMail to send them."));
809 
811  return 0;
812 }
813 
814 
815 //-----------------------------------------------------------------------------
816 KMMessage* KMFolderMbox::readMsg(int idx)
817 {
818  KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
819 
820  assert(mi!=0 && !mi->isMessage());
821  assert(mStream != 0);
822 
823  KMMessage *msg = new KMMessage(*mi);
824  msg->setMsgInfo( mi ); // remember the KMMsgInfo object to that we can restore it when the KMMessage object is no longer needed
825  mMsgList.set(idx,&msg->toMsgBase()); // done now so that the serial number can be computed
826  msg->fromDwString(getDwString(idx));
827  return msg;
828 }
829 
830 
831 #define STRDIM(x) (sizeof(x)/sizeof(*x)-1)
832 // performs (\n|^)>{n}From_ -> \1>{n-1}From_ conversion
833 static size_t unescapeFrom( char* str, size_t strLen ) {
834  if ( !str )
835  return 0;
836  if ( strLen <= STRDIM(">From ") )
837  return strLen;
838 
839  // yes, *d++ = *s++ is a no-op as long as d == s (until after the
840  // first >From_), but writes are cheap compared to reads and the
841  // data is already in the cache from the read, so special-casing
842  // might even be slower...
843  const char * s = str;
844  char * d = str;
845  const char * const e = str + strLen - STRDIM(">From ");
846 
847  while ( s < e ) {
848  if ( *s == '\n' && *(s+1) == '>' ) { // we can do the lookahead, since e is 6 chars from the end!
849  *d++ = *s++; // == '\n'
850  *d++ = *s++; // == '>'
851  while ( s < e && *s == '>' )
852  *d++ = *s++;
853  if ( tqstrncmp( s, "From ", STRDIM("From ") ) == 0 )
854  --d;
855  }
856  *d++ = *s++; // yes, s might be e here, but e is not the end :-)
857  }
858  // copy the rest:
859  while ( s < str + strLen )
860  *d++ = *s++;
861  if ( d < s ) // only NUL-terminate if it's shorter
862  *d = 0;
863 
864  return d - str;
865 }
866 
867 //static
868 TQByteArray KMFolderMbox::escapeFrom( const DwString & str ) {
869  const unsigned int strLen = str.length();
870  if ( strLen <= STRDIM("From ") )
871  return KMail::Util::ByteArray( str );
872  // worst case: \nFrom_\nFrom_\nFrom_... => grows to 7/6
873  TQByteArray result( int( strLen + 5 ) / 6 * 7 + 1 );
874 
875  const char * s = str.data();
876  const char * const e = s + strLen - STRDIM("From ");
877  char * d = result.data();
878 
879  bool onlyAnglesAfterLF = false; // dont' match ^From_
880  while ( s < e ) {
881  switch ( *s ) {
882  case '\n':
883  onlyAnglesAfterLF = true;
884  break;
885  case '>':
886  break;
887  case 'F':
888  if ( onlyAnglesAfterLF && tqstrncmp( s+1, "rom ", STRDIM("rom ") ) == 0 )
889  *d++ = '>';
890  // fall through
891  default:
892  onlyAnglesAfterLF = false;
893  break;
894  }
895  *d++ = *s++;
896  }
897  while ( s < str.data() + strLen )
898  *d++ = *s++;
899 
900  result.truncate( d - result.data() );
901  return result;
902 }
903 
904 #undef STRDIM
905 
906 //-----------------------------------------------------------------------------
907 DwString KMFolderMbox::getDwString(int idx)
908 {
909  KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
910 
911  assert(mi!=0);
912  assert(mStream != 0);
913 
914  size_t msgSize = mi->msgSize();
915  char* msgText = new char[ msgSize + 1 ];
916 
917  fseek(mStream, mi->folderOffset(), SEEK_SET);
918  fread(msgText, msgSize, 1, mStream);
919  msgText[msgSize] = '\0';
920 
921  size_t newMsgSize = unescapeFrom( msgText, msgSize );
922  newMsgSize = KMail::Util::crlf2lf( msgText, newMsgSize );
923 
924  DwString msgStr;
925  // the DwString takes possession of msgText, so we must not delete msgText
926  msgStr.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize );
927  return msgStr;
928 }
929 
930 
931 //-----------------------------------------------------------------------------
932 int KMFolderMbox::addMsg( KMMessage* aMsg, int* aIndex_ret )
933 {
934  if (!canAddMsgNow(aMsg, aIndex_ret)) return 0;
935  TQByteArray msgText;
936  char endStr[3];
937  int idx = -1, rc;
938  KMFolder* msgParent;
939  bool editing = false;
940  int growth = 0;
941 
942  KMFolderOpener openThis(folder(), "mboxaddMsg");
943  rc = openThis.openResult();
944  if (rc)
945  {
946  kdDebug(5006) << "KMFolderMbox::addMsg-open: " << rc << " of folder: " << label() << endl;
947  return rc;
948  }
949 
950  // take message out of the folder it is currently in, if any
951  msgParent = aMsg->parent();
952  if (msgParent)
953  {
954  if ( msgParent== folder() )
955  {
956  if (kmkernel->folderIsDraftOrOutbox( folder() ))
957  //special case for Edit message.
958  {
959  kdDebug(5006) << "Editing message in outbox or drafts" << endl;
960  editing = true;
961  }
962  else
963  return 0;
964  }
965 
966  idx = msgParent->find(aMsg);
967  msgParent->getMsg( idx );
968  }
969 
970  if (folderType() != KMFolderTypeImap)
971  {
972 /*
973 TQFile fileD0( "testdat_xx-kmfoldermbox-0" );
974 if( fileD0.open( IO_WriteOnly ) ) {
975  TQDataStream ds( &fileD0 );
976  ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
977  fileD0.close(); // If data is 0 we just create a zero length file.
978 }
979 */
980  aMsg->setStatusFields();
981 /*
982 TQFile fileD1( "testdat_xx-kmfoldermbox-1" );
983 if( fileD1.open( IO_WriteOnly ) ) {
984  TQDataStream ds( &fileD1 );
985  ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
986  fileD1.close(); // If data is 0 we just create a zero length file.
987 }
988 */
989  if (aMsg->headerField("Content-Type").isEmpty()) // This might be added by
990  aMsg->removeHeaderField("Content-Type"); // the line above
991  }
992  msgText = escapeFrom( aMsg->asDwString() );
993  size_t len = msgText.size();
994 
995  assert(mStream != 0);
996  clearerr(mStream);
997  if (len <= 0)
998  {
999  kdDebug(5006) << "Message added to folder `" << name() << "' contains no data. Ignoring it." << endl;
1000  return 0;
1001  }
1002 
1003  // Make sure the file is large enough to check for an end
1004  // character
1005  fseek(mStream, 0, SEEK_END);
1006  off_t revert = ftell(mStream);
1007  if (ftell(mStream) >= 2) {
1008  // write message to folder file
1009  fseek(mStream, -2, SEEK_END);
1010  fread(endStr, 1, 2, mStream); // ensure separating empty line
1011  if (ftell(mStream) > 0 && endStr[0]!='\n') {
1012  ++growth;
1013  if (endStr[1]!='\n') {
1014  //printf ("****endStr[1]=%c\n", endStr[1]);
1015  fwrite("\n\n", 1, 2, mStream);
1016  ++growth;
1017  }
1018  else fwrite("\n", 1, 1, mStream);
1019  }
1020  }
1021  fseek(mStream,0,SEEK_END); // this is needed on solaris and others
1022  int error = ferror(mStream);
1023  if (error)
1024  return error;
1025 
1026  TQCString messageSeparator( aMsg->mboxMessageSeparator() );
1027  fwrite( messageSeparator.data(), messageSeparator.length(), 1, mStream );
1028  off_t offs = ftell(mStream);
1029  fwrite(msgText.data(), len, 1, mStream);
1030  if (msgText[(int)len-1]!='\n') fwrite("\n\n", 1, 2, mStream);
1031  fflush(mStream);
1032  size_t size = ftell(mStream) - offs;
1033 
1034  error = ferror(mStream);
1035  if (error) {
1036  kdDebug(5006) << "Error: Could not add message to folder: " << strerror(errno) << endl;
1037  if (ftell(mStream) > revert) {
1038  kdDebug(5006) << "Undoing changes" << endl;
1039  truncate( TQFile::encodeName(location()), revert );
1040  }
1041  kmkernel->emergencyExit( i18n("Could not add message to folder: ") + TQString::fromLocal8Bit(strerror(errno)));
1042 
1043  /* This code is not 100% reliable
1044  bool busy = kmkernel->kbp()->isBusy();
1045  if (busy) kmkernel->kbp()->idle();
1046  KMessageBox::sorry(0,
1047  i18n("Unable to add message to folder.\n"
1048  "(No space left on device or insufficient quota?)\n"
1049  "Free space and sufficient quota are required to continue safely."));
1050  if (busy) kmkernel->kbp()->busy();
1051  kmkernel->kbp()->idle();
1052  */
1053  return error;
1054  }
1055 
1056  if (msgParent) {
1057  if (idx >= 0) msgParent->take(idx);
1058  }
1059 // if (mAccount) aMsg->removeHeaderField("X-UID");
1060 
1061  if (aMsg->isUnread() || aMsg->isNew() ||
1062  (folder() == kmkernel->outboxFolder())) {
1063  if (mUnreadMsgs == -1) mUnreadMsgs = 1;
1064  else ++mUnreadMsgs;
1065  if ( !mQuiet )
1066  emit numUnreadMsgsChanged( folder() );
1067  }
1068  ++mTotalMsgs;
1069  mSize = -1;
1070 
1071  if ( aMsg->attachmentState() == KMMsgAttachmentUnknown && aMsg->readyToShow() ) {
1072  aMsg->updateAttachmentState();
1073  }
1074  if ( aMsg->invitationState() == KMMsgInvitationUnknown && aMsg->readyToShow() ) {
1075  aMsg->updateInvitationState();
1076  }
1077 
1078  // store information about the position in the folder file in the message
1079  aMsg->setParent(folder());
1080  aMsg->setFolderOffset(offs);
1081  aMsg->setMsgSize(size);
1082  idx = mMsgList.append(&aMsg->toMsgBase(), mExportsSernums );
1083  if ( aMsg->getMsgSerNum() <= 0 )
1084  aMsg->setMsgSerNum();
1085  else
1086  replaceMsgSerNum( aMsg->getMsgSerNum(), &aMsg->toMsgBase(), idx );
1087 
1088  // change the length of the previous message to encompass white space added
1089  if ((idx > 0) && (growth > 0)) {
1090  // don't grow if a deleted message claims space at the end of the file
1091  if ((ulong)revert == mMsgList[idx - 1]->folderOffset() + mMsgList[idx - 1]->msgSize() )
1092  mMsgList[idx - 1]->setMsgSize( mMsgList[idx - 1]->msgSize() + growth );
1093  }
1094 
1095  // write index entry if desired
1096  if (mAutoCreateIndex)
1097  {
1098  assert(mIndexStream != 0);
1099  clearerr(mIndexStream);
1100  fseek(mIndexStream, 0, SEEK_END);
1101  revert = ftell(mIndexStream);
1102 
1103  KMMsgBase * mb = &aMsg->toMsgBase();
1104  int len;
1105  const uchar *buffer = mb->asIndexString(len);
1106  fwrite(&len,sizeof(len), 1, mIndexStream);
1107  mb->setIndexOffset( ftell(mIndexStream) );
1108  mb->setIndexLength( len );
1109  if(fwrite(buffer, len, 1, mIndexStream) != 1)
1110  kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
1111 
1112  fflush(mIndexStream);
1113  error = ferror(mIndexStream);
1114 
1115  if ( mExportsSernums )
1116  error |= appendToFolderIdsFile( idx );
1117 
1118  if (error) {
1119  kdWarning(5006) << "Error: Could not add message to folder (No space left on device?)" << endl;
1120  if (ftell(mIndexStream) > revert) {
1121  kdWarning(5006) << "Undoing changes" << endl;
1122  truncate( TQFile::encodeName(indexLocation()), revert );
1123  }
1124  if ( errno )
1125  kmkernel->emergencyExit( i18n("Could not add message to folder:") + TQString::fromLocal8Bit(strerror(errno)));
1126  else
1127  kmkernel->emergencyExit( i18n("Could not add message to folder (No space left on device?)") );
1128 
1129  /* This code may not be 100% reliable
1130  bool busy = kmkernel->kbp()->isBusy();
1131  if (busy) kmkernel->kbp()->idle();
1132  KMessageBox::sorry(0,
1133  i18n("Unable to add message to folder.\n"
1134  "(No space left on device or insufficient quota?)\n"
1135  "Free space and sufficient quota are required to continue safely."));
1136  if (busy) kmkernel->kbp()->busy();
1137  */
1138  return error;
1139  }
1140  }
1141 
1142  if (aIndex_ret) *aIndex_ret = idx;
1143  emitMsgAddedSignals(idx);
1144 
1145  // All streams have been flushed without errors if we arrive here
1146  // Return success!
1147  // (Don't return status of stream, it may have been closed already.)
1148  return 0;
1149 }
1150 
1151 int KMFolderMbox::compact( unsigned int startIndex, int nbMessages, FILE* tmpfile, off_t& offs, bool& done )
1152 {
1153  int rc = 0;
1154  TQCString mtext;
1155  unsigned int stopIndex = nbMessages == -1 ? mMsgList.count() :
1156  TQMIN( mMsgList.count(), startIndex + nbMessages );
1157  //kdDebug(5006) << "KMFolderMbox: compacting from " << startIndex << " to " << stopIndex << endl;
1158  for(unsigned int idx = startIndex; idx < stopIndex; ++idx) {
1159  KMMsgInfo* mi = (KMMsgInfo*)mMsgList.at(idx);
1160  size_t msize = mi->msgSize();
1161  if (mtext.size() < msize + 2)
1162  mtext.resize(msize+2);
1163  off_t folder_offset = mi->folderOffset();
1164 
1165  //now we need to find the separator! grr...
1166  for(off_t i = folder_offset-25; true; i -= 20) {
1167  off_t chunk_offset = i <= 0 ? 0 : i;
1168  if(fseek(mStream, chunk_offset, SEEK_SET) == -1) {
1169  rc = errno;
1170  break;
1171  }
1172  if (mtext.size() < 20)
1173  mtext.resize(20);
1174  fread(mtext.data(), 20, 1, mStream);
1175  if(i <= 0) { //woops we've reached the top of the file, last try..
1176  if ( mtext.contains( "from ", false ) ) {
1177  if (mtext.size() < (size_t)folder_offset)
1178  mtext.resize(folder_offset);
1179  if(fseek(mStream, chunk_offset, SEEK_SET) == -1 ||
1180  !fread(mtext.data(), folder_offset, 1, mStream) ||
1181  !fwrite(mtext.data(), folder_offset, 1, tmpfile)) {
1182  rc = errno;
1183  break;
1184  }
1185  offs += folder_offset;
1186  } else {
1187  rc = 666;
1188  }
1189  break;
1190  } else {
1191  int last_crlf = -1;
1192  for(int i2 = 0; i2 < 20; i2++) {
1193  if(*(mtext.data()+i2) == '\n')
1194  last_crlf = i2;
1195  }
1196  if(last_crlf != -1) {
1197  int size = folder_offset - (i + last_crlf+1);
1198  if ((int)mtext.size() < size)
1199  mtext.resize(size);
1200  if(fseek(mStream, i + last_crlf+1, SEEK_SET) == -1 ||
1201  !fread(mtext.data(), size, 1, mStream) ||
1202  !fwrite(mtext.data(), size, 1, tmpfile)) {
1203  rc = errno;
1204  break;
1205  }
1206  offs += size;
1207  break;
1208  }
1209  }
1210  }
1211  if (rc)
1212  break;
1213 
1214  //now actually write the message
1215  if(fseek(mStream, folder_offset, SEEK_SET) == -1 ||
1216  !fread(mtext.data(), msize, 1, mStream) || !fwrite(mtext.data(), msize, 1, tmpfile)) {
1217  rc = errno;
1218  break;
1219  }
1220  mi->setFolderOffset(offs);
1221  offs += msize;
1222  }
1223  done = ( !rc && stopIndex == mMsgList.count() ); // finished without errors
1224  return rc;
1225 }
1226 
1227 //-----------------------------------------------------------------------------
1228 int KMFolderMbox::compact( bool silent )
1229 {
1230  // This is called only when the user explicitely requests compaction,
1231  // so we don't check needsCompact.
1232 
1233  KMail::MboxCompactionJob* job = new KMail::MboxCompactionJob( folder(), true /*immediate*/ );
1234  int rc = job->executeNow( silent );
1235  // Note that job autodeletes itself.
1236 
1237  // If this is the current folder, the changed signal will ultimately call
1238  // KMHeaders::setFolderInfoStatus which will override the message, so save/restore it
1239  TQString statusMsg = BroadcastStatus::instance()->statusMsg();
1240  emit changed();
1241  BroadcastStatus::instance()->setStatusMsg( statusMsg );
1242  return rc;
1243 }
1244 
1245 
1246 //-----------------------------------------------------------------------------
1247 void KMFolderMbox::setLockType( LockType ltype )
1248 {
1249  mLockType = ltype;
1250 }
1251 
1252 //-----------------------------------------------------------------------------
1253 void KMFolderMbox::setProcmailLockFileName( const TQString &fname )
1254 {
1255  mProcmailLockFileName = fname;
1256 }
1257 
1258 //-----------------------------------------------------------------------------
1259 int KMFolderMbox::removeContents()
1260 {
1261  int rc = 0;
1262  rc = unlink(TQFile::encodeName(location()));
1263  return rc;
1264 }
1265 
1266 //-----------------------------------------------------------------------------
1267 int KMFolderMbox::expungeContents()
1268 {
1269  int rc = 0;
1270  if (truncate(TQFile::encodeName(location()), 0))
1271  rc = errno;
1272  return rc;
1273 }
1274 
1275 //-----------------------------------------------------------------------------
1276 /*virtual*/
1277 TQ_INT64 KMFolderMbox::doFolderSize() const
1278 {
1279  TQFileInfo info( location() );
1280  return (TQ_INT64)(info.size());
1281 }
1282 
1283 //-----------------------------------------------------------------------------
1284 #include "kmfoldermbox.moc"
A job that runs in the background and compacts mbox folders.
Definition: compactionjob.h:39
off_t mHeaderOffset
offset of header of index file
void invalidateFolder()
Called when serial numbers for a folder are invalidated, invalidates/recreates data structures depend...
A FolderStorage with an index for faster access to often used message properties. ...
Definition: kmfolderindex.h:37
virtual bool noContent() const
Returns, if the folder can&#39;t contain mails, but only subfolder.
KMMessage * take(int idx)
Detach message from this folder.
Definition: kmfolder.cpp:380
IndexStatus
This enum indicates the status of the index file.
Definition: kmfolderindex.h:50
RAII for KMFolder::open() / close().
Definition: kmfolder.h:688
virtual int count(bool cache=false) const
Number of messages in this folder.
void clear(bool autoDelete=TRUE, bool syncDict=false)
Clear messages.
Definition: kmmsglist.cpp:32
virtual int updateIndex()
Incrementally update the index if possible else call writeIndex.
int find(const KMMsgBase *msg) const
Returns the index of the given message or -1 if not found.
Definition: kmfolder.cpp:435
bool readyToShow() const
Return if the message is ready to be shown.
Definition: kmmessage.h:872
KMMsgBase & toMsgBase()
Get KMMsgBase for this object.
Definition: kmmessage.h:114
virtual TQString indexLocation() const
Returns full path to index file.
FILE * mIndexStream
table of contents file
void setStatusFields()
Set "Status" and "X-Status" fields of the message from the internal message status.
Definition: kmmessage.cpp:353
TQString location() const
Returns full path to folder file.
virtual bool canAddMsgNow(KMMessage *aMsg, int *aIndex_ret)
Returns FALSE, if the message has to be retrieved from an IMAP account first.
bool mAutoCreateIndex
is the automatic creation of a index file allowed ?
TQString headerField(const TQCString &name) const
Returns the value of a header field with the given name.
Definition: kmmessage.cpp:2291
virtual DwString getDwString(int idx)=0
Read a message and returns a DwString.
size_t crlf2lf(char *str, const size_t strLen)
Convert all sequences of "\r\n" (carriage return followed by a line feed) to a single "\n" (line feed...
Definition: util.cpp:44
bool readIndex()
Read index file and fill the message-info list mMsgList.
void setMsgSerNum(unsigned long newMsgSerNum=0)
Sets the message serial number.
Definition: kmmessage.cpp:225
void statusMsg(const TQString &)
Emmited to display a message somewhere in a status line.
unsigned int count() const
Number of messages in the array.
Definition: kmmsglist.h:86
TQString label() const
Returns the label of the folder for visualization.
void numUnreadMsgsChanged(KMFolder *)
Emitted when number of unread messages has changed.
void setMsgInfo(KMMsgInfo *msgInfo)
Set the KMMsgInfo object corresponding to this message.
Definition: kmmessage.h:932
virtual int writeIndex(bool createEmptyIndex=false)
Write index to index-file.
bool reset(unsigned int size)
Clear the array and resize it to given size.
Definition: kmmsglist.cpp:83
Mail folder.
Definition: kmfolder.h:68
void changed()
Emitted when the status, name, or associated accounts of this folder changed.
void close(const char *owner, bool force=false)
Close folder.
virtual void writeConfig()
Write the config file.
bool mFilesLocked
TRUE if the files of the folder are locked (writable)
KMMsgList mMsgList
list of index entries or messages
void fromDwString(const DwString &str, bool setStatus=false)
Parse the string and create this message from it.
Definition: kmmessage.cpp:404
void replaceMsgSerNum(unsigned long sernum, KMMsgBase *msg, int idx)
Replaces the serial number for the message msg at index idx with sernum.
int mUnreadMsgs
number of unread messages, -1 if not yet set
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
sets a cursor and makes sure it&#39;s restored on destruction Create a KCursorSaver object when you want ...
Definition: kcursorsaver.h:13
virtual KMFolderType folderType() const
Returns the type of this folder.
Definition: folderstorage.h:96
TQByteArray ByteArray(const DwString &str)
Construct a TQByteArray from a DwString.
Definition: util.cpp:122
virtual int createIndexFromContents()=0
Create index file from messages file and fill the message-info list mMsgList.
unsigned int append(KMMsgBase *msg, bool syncDict=true)
Append given message after the last used message.
Definition: kmmsglist.cpp:131
bool mCompactable
false if index file is out of sync with mbox file
This is a Mime Message.
Definition: kmmessage.h:67
void set(unsigned int idx, KMMsgBase *msg)
Set message at given index.
Definition: kmmsglist.cpp:92
bool resize(unsigned int size)
Resize array and initialize new elements if any.
Definition: kmmsglist.cpp:51
int appendToFolderIdsFile(int idx=-1)
Append message to end of message serial number file.
TQCString mboxMessageSeparator()
Returns an mbox message separator line for this message, i.e.
Definition: kmmessage.cpp:4483
virtual void correctUnreadMsgsCount()
A cludge to help make sure the count of unread messges is kept in sync.
void removeHeaderField(const TQCString &name)
Remove header field with given name.
Definition: kmmessage.cpp:2319
virtual IndexStatus indexStatus()=0
Tests whether the contents of this folder is newer than the index.
virtual int addMsg(TQPtrList< KMMessage > &, TQValueList< int > &index_return)
Adds the given messages to the folder.
const DwString & asDwString() const
Return the entire message contents in the DwString.
Definition: kmmessage.cpp:294
bool mExportsSernums
Has this storage exported its serial numbers to the global message dict for lookup?
void emitMsgAddedSignals(int idx)
Called by derived classes implementation of addMsg.