kmail

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