You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1279 lines
38 KiB
1279 lines
38 KiB
15 years ago
|
/* -*- c-basic-offset: 2 -*-
|
||
|
* kmail: KDE mail client
|
||
|
* Copyright (c) 1996-1998 Stefan Taferner <taferner@kde.org>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*
|
||
|
*/
|
||
|
#include <config.h>
|
||
|
#include <qfileinfo.h>
|
||
|
#include <qregexp.h>
|
||
|
|
||
|
#include "kmfoldermbox.h"
|
||
|
#include "folderstorage.h"
|
||
|
#include "kmfolder.h"
|
||
|
#include "kmkernel.h"
|
||
|
#include "kmmsgdict.h"
|
||
|
#include "undostack.h"
|
||
|
#include "kcursorsaver.h"
|
||
|
#include "jobscheduler.h"
|
||
|
#include "compactionjob.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
#include <kdebug.h>
|
||
|
#include <klocale.h>
|
||
|
#include <kmessagebox.h>
|
||
|
#include <knotifyclient.h>
|
||
|
#include <kprocess.h>
|
||
|
#include <kconfig.h>
|
||
|
|
||
|
#include <ctype.h>
|
||
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
#include <assert.h>
|
||
|
#include <ctype.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#ifdef HAVE_FCNTL_H
|
||
|
#include <fcntl.h>
|
||
|
#endif
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/file.h>
|
||
|
#include "broadcaststatus.h"
|
||
|
using KPIM::BroadcastStatus;
|
||
|
|
||
|
#ifndef MAX_LINE
|
||
|
#define MAX_LINE 4096
|
||
|
#endif
|
||
|
#ifndef INIT_MSGS
|
||
|
#define INIT_MSGS 8
|
||
|
#endif
|
||
|
|
||
|
// Regular expression to find the line that seperates messages in a mail
|
||
|
// folder:
|
||
|
#define MSG_SEPERATOR_START "From "
|
||
|
#define MSG_SEPERATOR_START_LEN (sizeof(MSG_SEPERATOR_START) - 1)
|
||
|
#define MSG_SEPERATOR_REGEX "^From .*[0-9][0-9]:[0-9][0-9]"
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
KMFolderMbox::KMFolderMbox(KMFolder* folder, const char* name)
|
||
|
: KMFolderIndex(folder, name)
|
||
|
{
|
||
|
mStream = 0;
|
||
|
mFilesLocked = false;
|
||
|
mReadOnly = false;
|
||
|
mLockType = lock_none;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
KMFolderMbox::~KMFolderMbox()
|
||
|
{
|
||
|
if (mOpenCount>0)
|
||
|
close("~kmfoldermbox", true);
|
||
|
if (kmkernel->undoStack())
|
||
|
kmkernel->undoStack()->folderDestroyed( folder() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::open(const char *owner)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
|
||
|
mOpenCount++;
|
||
|
kmkernel->jobScheduler()->notifyOpeningFolder( folder() );
|
||
|
|
||
|
if (mOpenCount > 1) return 0; // already open
|
||
|
|
||
|
assert(!folder()->name().isEmpty());
|
||
|
|
||
|
mFilesLocked = false;
|
||
|
mStream = fopen(QFile::encodeName(location()), "r+"); // messages file
|
||
|
if (!mStream)
|
||
|
{
|
||
|
KNotifyClient::event( 0, "warning",
|
||
|
i18n("Cannot open file \"%1\":\n%2").arg(location()).arg(strerror(errno)));
|
||
|
kdDebug(5006) << "Cannot open folder `" << location() << "': " << strerror(errno) << endl;
|
||
|
mOpenCount = 0;
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
lock();
|
||
|
|
||
|
if (!folder()->path().isEmpty())
|
||
|
{
|
||
|
KMFolderIndex::IndexStatus index_status = indexStatus();
|
||
|
// test if index file exists and is up-to-date
|
||
|
if (KMFolderIndex::IndexOk != index_status)
|
||
|
{
|
||
|
// only show a warning if the index file exists, otherwise it can be
|
||
|
// silently regenerated
|
||
|
if (KMFolderIndex::IndexTooOld == index_status) {
|
||
|
QString msg = i18n("<qt><p>The index of folder '%2' seems "
|
||
|
"to be out of date. To prevent message "
|
||
|
"corruption the index will be "
|
||
|
"regenerated. As a result deleted "
|
||
|
"messages might reappear and status "
|
||
|
"flags might be lost.</p>"
|
||
|
"<p>Please read the corresponding entry "
|
||
|
"in the <a href=\"%1\">FAQ section of the manual "
|
||
|
"of KMail</a> for "
|
||
|
"information about how to prevent this "
|
||
|
"problem from happening again.</p></qt>")
|
||
|
.arg("help:/kmail/faq.html#faq-index-regeneration")
|
||
|
.arg(name());
|
||
|
// When KMail is starting up we have to show a non-blocking message
|
||
|
// box so that the initialization can continue. We don't show a
|
||
|
// queued message box when KMail isn't starting up because queued
|
||
|
// message boxes don't have a "Don't ask again" checkbox.
|
||
|
if (kmkernel->startingUp())
|
||
|
{
|
||
|
KConfigGroup configGroup( KMKernel::config(), "Notification Messages" );
|
||
|
bool showMessage =
|
||
|
configGroup.readBoolEntry( "showIndexRegenerationMessage", true );
|
||
|
if (showMessage)
|
||
|
KMessageBox::queuedMessageBox( 0, KMessageBox::Information,
|
||
|
msg, i18n("Index Out of Date"),
|
||
|
KMessageBox::AllowLink );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
KCursorSaver idle(KBusyPtr::idle());
|
||
|
KMessageBox::information( 0, msg, i18n("Index Out of Date"),
|
||
|
"showIndexRegenerationMessage",
|
||
|
KMessageBox::AllowLink );
|
||
|
}
|
||
|
}
|
||
|
QString str;
|
||
|
mIndexStream = 0;
|
||
|
str = i18n("Folder `%1' changed. Recreating index.")
|
||
|
.arg(name());
|
||
|
emit statusMsg(str);
|
||
|
} else {
|
||
|
mIndexStream = fopen(QFile::encodeName(indexLocation()), "r+"); // index file
|
||
|
if ( mIndexStream ) {
|
||
|
fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
|
||
|
updateIndexStreamPtr();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!mIndexStream)
|
||
|
rc = createIndexFromContents();
|
||
|
else
|
||
|
if (!readIndex())
|
||
|
rc = createIndexFromContents();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mAutoCreateIndex = false;
|
||
|
rc = createIndexFromContents();
|
||
|
}
|
||
|
|
||
|
mChanged = false;
|
||
|
|
||
|
fcntl(fileno(mStream), F_SETFD, FD_CLOEXEC);
|
||
|
if (mIndexStream)
|
||
|
fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::canAccess()
|
||
|
{
|
||
|
assert(!folder()->name().isEmpty());
|
||
|
|
||
|
if (access(QFile::encodeName(location()), R_OK | W_OK) != 0) {
|
||
|
kdDebug(5006) << "KMFolderMbox::access call to access function failed" << endl;
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::create()
|
||
|
{
|
||
|
int rc;
|
||
|
int old_umask;
|
||
|
|
||
|
assert(!folder()->name().isEmpty());
|
||
|
assert(mOpenCount == 0);
|
||
|
|
||
|
kdDebug(5006) << "Creating folder " << name() << endl;
|
||
|
if (access(QFile::encodeName(location()), F_OK) == 0) {
|
||
|
kdDebug(5006) << "KMFolderMbox::create call to access function failed." << endl;
|
||
|
kdDebug(5006) << "File:: " << endl;
|
||
|
kdDebug(5006) << "Error " << endl;
|
||
|
return EEXIST;
|
||
|
}
|
||
|
|
||
|
old_umask = umask(077);
|
||
|
mStream = fopen(QFile::encodeName(location()), "w+"); //sven; open RW
|
||
|
umask(old_umask);
|
||
|
|
||
|
if (!mStream) return errno;
|
||
|
|
||
|
fcntl(fileno(mStream), F_SETFD, FD_CLOEXEC);
|
||
|
|
||
|
if (!folder()->path().isEmpty())
|
||
|
{
|
||
|
old_umask = umask(077);
|
||
|
mIndexStream = fopen(QFile::encodeName(indexLocation()), "w+"); //sven; open RW
|
||
|
updateIndexStreamPtr(true);
|
||
|
umask(old_umask);
|
||
|
|
||
|
if (!mIndexStream) return errno;
|
||
|
fcntl(fileno(mIndexStream), F_SETFD, FD_CLOEXEC);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mAutoCreateIndex = false;
|
||
|
}
|
||
|
|
||
|
mOpenCount++;
|
||
|
mChanged = false;
|
||
|
|
||
|
rc = writeIndex();
|
||
|
if (!rc) lock();
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void KMFolderMbox::reallyDoClose(const char* owner)
|
||
|
{
|
||
|
if (mAutoCreateIndex)
|
||
|
{
|
||
|
if (KMFolderIndex::IndexOk != indexStatus()) {
|
||
|
kdDebug(5006) << "Critical error: " << location() <<
|
||
|
" has been modified by an external application while KMail was running." << endl;
|
||
|
// exit(1); backed out due to broken nfs
|
||
|
}
|
||
|
|
||
|
updateIndex();
|
||
|
writeConfig();
|
||
|
}
|
||
|
|
||
|
if (!noContent()) {
|
||
|
if (mStream) unlock();
|
||
|
mMsgList.clear(true);
|
||
|
|
||
|
if (mStream) fclose(mStream);
|
||
|
if (mIndexStream) {
|
||
|
fclose(mIndexStream);
|
||
|
updateIndexStreamPtr(true);
|
||
|
}
|
||
|
}
|
||
|
mOpenCount = 0;
|
||
|
mStream = 0;
|
||
|
mIndexStream = 0;
|
||
|
mFilesLocked = false;
|
||
|
mUnreadMsgs = -1;
|
||
|
|
||
|
mMsgList.reset(INIT_MSGS);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void KMFolderMbox::sync()
|
||
|
{
|
||
|
if (mOpenCount > 0)
|
||
|
if (!mStream || fsync(fileno(mStream)) ||
|
||
|
!mIndexStream || fsync(fileno(mIndexStream))) {
|
||
|
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.")));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::lock()
|
||
|
{
|
||
|
int rc;
|
||
|
struct flock fl;
|
||
|
fl.l_type=F_WRLCK;
|
||
|
fl.l_whence=0;
|
||
|
fl.l_start=0;
|
||
|
fl.l_len=0;
|
||
|
fl.l_pid=-1;
|
||
|
QCString cmd_str;
|
||
|
assert(mStream != 0);
|
||
|
mFilesLocked = false;
|
||
|
mReadOnly = false;
|
||
|
|
||
|
switch( mLockType )
|
||
|
{
|
||
|
case FCNTL:
|
||
|
rc = fcntl(fileno(mStream), F_SETLKW, &fl);
|
||
|
|
||
|
if (rc < 0)
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock folder `" << location() << "': "
|
||
|
<< strerror(errno) << " (" << errno << ")" << endl;
|
||
|
mReadOnly = true;
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
if (mIndexStream)
|
||
|
{
|
||
|
rc = fcntl(fileno(mIndexStream), F_SETLK, &fl);
|
||
|
|
||
|
if (rc < 0)
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
|
||
|
<< strerror(errno) << " (" << errno << ")" << endl;
|
||
|
rc = errno;
|
||
|
fl.l_type = F_UNLCK;
|
||
|
/*rc =*/ fcntl(fileno(mIndexStream), F_SETLK, &fl);
|
||
|
mReadOnly = true;
|
||
|
return rc;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case procmail_lockfile:
|
||
|
cmd_str = "lockfile -l20 -r5 ";
|
||
|
if (!mProcmailLockFileName.isEmpty())
|
||
|
cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName));
|
||
|
else
|
||
|
cmd_str += QFile::encodeName(KProcess::quote(location() + ".lock"));
|
||
|
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( rc != 0 )
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock folder `" << location() << "': "
|
||
|
<< strerror(rc) << " (" << rc << ")" << endl;
|
||
|
mReadOnly = true;
|
||
|
return rc;
|
||
|
}
|
||
|
if( mIndexStream )
|
||
|
{
|
||
|
cmd_str = "lockfile -l20 -r5 " + QFile::encodeName(KProcess::quote(indexLocation() + ".lock"));
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( rc != 0 )
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
|
||
|
<< strerror(rc) << " (" << rc << ")" << endl;
|
||
|
mReadOnly = true;
|
||
|
return rc;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case mutt_dotlock:
|
||
|
cmd_str = "mutt_dotlock " + QFile::encodeName(KProcess::quote(location()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( rc != 0 )
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock folder `" << location() << "': "
|
||
|
<< strerror(rc) << " (" << rc << ")" << endl;
|
||
|
mReadOnly = true;
|
||
|
return rc;
|
||
|
}
|
||
|
if( mIndexStream )
|
||
|
{
|
||
|
cmd_str = "mutt_dotlock " + QFile::encodeName(KProcess::quote(indexLocation()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( rc != 0 )
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
|
||
|
<< strerror(rc) << " (" << rc << ")" << endl;
|
||
|
mReadOnly = true;
|
||
|
return rc;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case mutt_dotlock_privileged:
|
||
|
cmd_str = "mutt_dotlock -p " + QFile::encodeName(KProcess::quote(location()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( rc != 0 )
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock folder `" << location() << "': "
|
||
|
<< strerror(rc) << " (" << rc << ")" << endl;
|
||
|
mReadOnly = true;
|
||
|
return rc;
|
||
|
}
|
||
|
if( mIndexStream )
|
||
|
{
|
||
|
cmd_str = "mutt_dotlock -p " + QFile::encodeName(KProcess::quote(indexLocation()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( rc != 0 )
|
||
|
{
|
||
|
kdDebug(5006) << "Cannot lock index of folder `" << location() << "': "
|
||
|
<< strerror(rc) << " (" << rc << ")" << endl;
|
||
|
mReadOnly = true;
|
||
|
return rc;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case lock_none:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
mFilesLocked = true;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------
|
||
|
FolderJob*
|
||
|
KMFolderMbox::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
|
||
|
KMFolder *folder, QString, const AttachmentStrategy* ) const
|
||
|
{
|
||
|
MboxJob *job = new MboxJob( msg, jt, folder );
|
||
|
job->setParent( this );
|
||
|
return job;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------
|
||
|
FolderJob*
|
||
|
KMFolderMbox::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
|
||
|
FolderJob::JobType jt, KMFolder *folder ) const
|
||
|
{
|
||
|
MboxJob *job = new MboxJob( msgList, sets, jt, folder );
|
||
|
job->setParent( this );
|
||
|
return job;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::unlock()
|
||
|
{
|
||
|
int rc;
|
||
|
struct flock fl;
|
||
|
fl.l_type=F_UNLCK;
|
||
|
fl.l_whence=0;
|
||
|
fl.l_start=0;
|
||
|
fl.l_len=0;
|
||
|
QCString cmd_str;
|
||
|
|
||
|
assert(mStream != 0);
|
||
|
mFilesLocked = false;
|
||
|
|
||
|
switch( mLockType )
|
||
|
{
|
||
|
case FCNTL:
|
||
|
if (mIndexStream) fcntl(fileno(mIndexStream), F_SETLK, &fl);
|
||
|
fcntl(fileno(mStream), F_SETLK, &fl);
|
||
|
rc = errno;
|
||
|
break;
|
||
|
|
||
|
case procmail_lockfile:
|
||
|
cmd_str = "rm -f ";
|
||
|
if (!mProcmailLockFileName.isEmpty())
|
||
|
cmd_str += QFile::encodeName(KProcess::quote(mProcmailLockFileName));
|
||
|
else
|
||
|
cmd_str += QFile::encodeName(KProcess::quote(location() + ".lock"));
|
||
|
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( mIndexStream )
|
||
|
{
|
||
|
cmd_str = "rm -f " + QFile::encodeName(KProcess::quote(indexLocation() + ".lock"));
|
||
|
rc = system( cmd_str.data() );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case mutt_dotlock:
|
||
|
cmd_str = "mutt_dotlock -u " + QFile::encodeName(KProcess::quote(location()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( mIndexStream )
|
||
|
{
|
||
|
cmd_str = "mutt_dotlock -u " + QFile::encodeName(KProcess::quote(indexLocation()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case mutt_dotlock_privileged:
|
||
|
cmd_str = "mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(location()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
if( mIndexStream )
|
||
|
{
|
||
|
cmd_str = "mutt_dotlock -p -u " + QFile::encodeName(KProcess::quote(indexLocation()));
|
||
|
rc = system( cmd_str.data() );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case lock_none:
|
||
|
default:
|
||
|
rc = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
KMFolderIndex::IndexStatus KMFolderMbox::indexStatus()
|
||
|
{
|
||
|
QFileInfo contInfo(location());
|
||
|
QFileInfo indInfo(indexLocation());
|
||
|
|
||
|
if (!contInfo.exists()) return KMFolderIndex::IndexOk;
|
||
|
if (!indInfo.exists()) return KMFolderIndex::IndexMissing;
|
||
|
|
||
|
// Check whether the mbox file is more than 5 seconds newer than the index
|
||
|
// file. The 5 seconds are added to reduce the number of false alerts due
|
||
|
// to slightly out of sync clocks of the NFS server and the local machine.
|
||
|
return ( contInfo.lastModified() > indInfo.lastModified().addSecs(5) )
|
||
|
? KMFolderIndex::IndexTooOld
|
||
|
: KMFolderIndex::IndexOk;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::createIndexFromContents()
|
||
|
{
|
||
|
char line[MAX_LINE];
|
||
|
char status[8], xstatus[8];
|
||
|
QCString subjStr, dateStr, fromStr, toStr, xmarkStr, *lastStr=0;
|
||
|
QCString replyToIdStr, replyToAuxIdStr, referencesStr, msgIdStr;
|
||
|
QCString sizeServerStr, uidStr;
|
||
|
QCString contentTypeStr, charset;
|
||
|
bool atEof = false;
|
||
|
bool inHeader = true;
|
||
|
KMMsgInfo* mi;
|
||
|
QString msgStr;
|
||
|
QRegExp regexp(MSG_SEPERATOR_REGEX);
|
||
|
int i, num, numStatus;
|
||
|
short needStatus;
|
||
|
|
||
|
assert(mStream != 0);
|
||
|
rewind(mStream);
|
||
|
|
||
|
mMsgList.clear();
|
||
|
|
||
|
num = -1;
|
||
|
numStatus= 11;
|
||
|
off_t offs = 0;
|
||
|
size_t size = 0;
|
||
|
dateStr = "";
|
||
|
fromStr = "";
|
||
|
toStr = "";
|
||
|
subjStr = "";
|
||
|
*status = '\0';
|
||
|
*xstatus = '\0';
|
||
|
xmarkStr = "";
|
||
|
replyToIdStr = "";
|
||
|
replyToAuxIdStr = "";
|
||
|
referencesStr = "";
|
||
|
msgIdStr = "";
|
||
|
needStatus = 3;
|
||
|
size_t sizeServer = 0;
|
||
|
ulong uid = 0;
|
||
|
|
||
|
|
||
|
while (!atEof)
|
||
|
{
|
||
|
off_t pos = ftell(mStream);
|
||
|
if (!fgets(line, MAX_LINE, mStream)) atEof = true;
|
||
|
|
||
|
if (atEof ||
|
||
|
(memcmp(line, MSG_SEPERATOR_START, MSG_SEPERATOR_START_LEN)==0 &&
|
||
|
regexp.search(line) >= 0))
|
||
|
{
|
||
|
size = pos - offs;
|
||
|
pos = ftell(mStream);
|
||
|
|
||
|
if (num >= 0)
|
||
|
{
|
||
|
if (numStatus <= 0)
|
||
|
{
|
||
|
msgStr = i18n("Creating index file: one message done", "Creating index file: %n messages done", num);
|
||
|
emit statusMsg(msgStr);
|
||
|
numStatus = 10;
|
||
|
}
|
||
|
|
||
|
if (size > 0)
|
||
|
{
|
||
|
msgIdStr = msgIdStr.stripWhiteSpace();
|
||
|
if( !msgIdStr.isEmpty() ) {
|
||
|
int rightAngle;
|
||
|
rightAngle = msgIdStr.find( '>' );
|
||
|
if( rightAngle != -1 )
|
||
|
msgIdStr.truncate( rightAngle + 1 );
|
||
|
}
|
||
|
|
||
|
replyToIdStr = replyToIdStr.stripWhiteSpace();
|
||
|
if( !replyToIdStr.isEmpty() ) {
|
||
|
int rightAngle;
|
||
|
rightAngle = replyToIdStr.find( '>' );
|
||
|
if( rightAngle != -1 )
|
||
|
replyToIdStr.truncate( rightAngle + 1 );
|
||
|
}
|
||
|
|
||
|
referencesStr = referencesStr.stripWhiteSpace();
|
||
|
if( !referencesStr.isEmpty() ) {
|
||
|
int leftAngle, rightAngle;
|
||
|
leftAngle = referencesStr.findRev( '<' );
|
||
|
if( ( leftAngle != -1 )
|
||
|
&& ( replyToIdStr.isEmpty() || ( replyToIdStr[0] != '<' ) ) ) {
|
||
|
// use the last reference, instead of missing In-Reply-To
|
||
|
replyToIdStr = referencesStr.mid( leftAngle );
|
||
|
}
|
||
|
|
||
|
// find second last reference
|
||
|
leftAngle = referencesStr.findRev( '<', leftAngle - 1 );
|
||
|
if( leftAngle != -1 )
|
||
|
referencesStr = referencesStr.mid( leftAngle );
|
||
|
rightAngle = referencesStr.findRev( '>' );
|
||
|
if( rightAngle != -1 )
|
||
|
referencesStr.truncate( rightAngle + 1 );
|
||
|
|
||
|
// Store the second to last reference in the replyToAuxIdStr
|
||
|
// It is a good candidate for threading the message below if the
|
||
|
// message In-Reply-To points to is not kept in this folder,
|
||
|
// but e.g. in an Outbox
|
||
|
replyToAuxIdStr = referencesStr;
|
||
|
rightAngle = referencesStr.find( '>' );
|
||
|
if( rightAngle != -1 )
|
||
|
replyToAuxIdStr.truncate( rightAngle + 1 );
|
||
|
}
|
||
|
|
||
|
contentTypeStr = contentTypeStr.stripWhiteSpace();
|
||
|
charset = "";
|
||
|
if ( !contentTypeStr.isEmpty() )
|
||
|
{
|
||
|
int cidx = contentTypeStr.find( "charset=" );
|
||
|
if ( cidx != -1 ) {
|
||
|
charset = contentTypeStr.mid( cidx + 8 );
|
||
|
if ( !charset.isEmpty() && ( charset[0] == '"' ) ) {
|
||
|
charset = charset.mid( 1 );
|
||
|
}
|
||
|
cidx = 0;
|
||
|
while ( (unsigned int) cidx < charset.length() ) {
|
||
|
if ( charset[cidx] == '"' || ( !isalnum(charset[cidx]) &&
|
||
|
charset[cidx] != '-' && charset[cidx] != '_' ) )
|
||
|
break;
|
||
|
++cidx;
|
||
|
}
|
||
|
charset.truncate( cidx );
|
||
|
// kdDebug() << "KMFolderMaildir::readFileHeaderIntern() charset found: " <<
|
||
|
// charset << " from " << contentTypeStr << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mi = new KMMsgInfo(folder());
|
||
|
mi->init( subjStr.stripWhiteSpace(),
|
||
|
fromStr.stripWhiteSpace(),
|
||
|
toStr.stripWhiteSpace(),
|
||
|
0, KMMsgStatusNew,
|
||
|
xmarkStr.stripWhiteSpace(),
|
||
|
replyToIdStr, replyToAuxIdStr, msgIdStr,
|
||
|
KMMsgEncryptionStateUnknown, KMMsgSignatureStateUnknown,
|
||
|
KMMsgMDNStateUnknown, charset, offs, size, sizeServer, uid );
|
||
|
mi->setStatus(status, xstatus);
|
||
|
mi->setDate( dateStr.stripWhiteSpace() );
|
||
|
mi->setDirty(false);
|
||
|
mMsgList.append(mi, mExportsSernums );
|
||
|
|
||
|
*status = '\0';
|
||
|
*xstatus = '\0';
|
||
|
needStatus = 3;
|
||
|
xmarkStr = "";
|
||
|
replyToIdStr = "";
|
||
|
replyToAuxIdStr = "";
|
||
|
referencesStr = "";
|
||
|
msgIdStr = "";
|
||
|
dateStr = "";
|
||
|
fromStr = "";
|
||
|
subjStr = "";
|
||
|
sizeServer = 0;
|
||
|
uid = 0;
|
||
|
}
|
||
|
else num--,numStatus++;
|
||
|
}
|
||
|
|
||
|
offs = ftell(mStream);
|
||
|
num++;
|
||
|
numStatus--;
|
||
|
inHeader = true;
|
||
|
continue;
|
||
|
}
|
||
|
// Is this a long header line?
|
||
|
if (inHeader && (line[0]=='\t' || line[0]==' '))
|
||
|
{
|
||
|
i = 0;
|
||
|
while (line [i]=='\t' || line [i]==' ') i++;
|
||
|
if (line [i] < ' ' && line [i]>0) inHeader = false;
|
||
|
else if (lastStr) *lastStr += line + i;
|
||
|
}
|
||
|
else lastStr = 0;
|
||
|
|
||
|
if (inHeader && (line [0]=='\n' || line [0]=='\r'))
|
||
|
inHeader = false;
|
||
|
if (!inHeader) continue;
|
||
|
|
||
|
/* -sanders Make all messages read when auto-recreating index */
|
||
|
/* Reverted, as it breaks reading the sent mail status, for example.
|
||
|
-till */
|
||
|
if ((needStatus & 1) && strncasecmp(line, "Status:", 7) == 0)
|
||
|
{
|
||
|
for(i=0; i<4 && line[i+8] > ' '; i++)
|
||
|
status[i] = line[i+8];
|
||
|
status[i] = '\0';
|
||
|
needStatus &= ~1;
|
||
|
}
|
||
|
else if ((needStatus & 2) && strncasecmp(line, "X-Status:", 9)==0)
|
||
|
{
|
||
|
for(i=0; i<4 && line[i+10] > ' '; i++)
|
||
|
xstatus[i] = line[i+10];
|
||
|
xstatus[i] = '\0';
|
||
|
needStatus &= ~2;
|
||
|
}
|
||
|
else if (strncasecmp(line,"X-KMail-Mark:",13)==0)
|
||
|
xmarkStr = QCString(line+13);
|
||
|
else if (strncasecmp(line,"In-Reply-To:",12)==0) {
|
||
|
replyToIdStr = QCString(line+12);
|
||
|
lastStr = &replyToIdStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"References:",11)==0) {
|
||
|
referencesStr = QCString(line+11);
|
||
|
lastStr = &referencesStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"Message-Id:",11)==0) {
|
||
|
msgIdStr = QCString(line+11);
|
||
|
lastStr = &msgIdStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"Date:",5)==0)
|
||
|
{
|
||
|
dateStr = QCString(line+5);
|
||
|
lastStr = &dateStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"From:", 5)==0)
|
||
|
{
|
||
|
fromStr = QCString(line+5);
|
||
|
lastStr = &fromStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"To:", 3)==0)
|
||
|
{
|
||
|
toStr = QCString(line+3);
|
||
|
lastStr = &toStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"Subject:",8)==0)
|
||
|
{
|
||
|
subjStr = QCString(line+8);
|
||
|
lastStr = &subjStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"X-Length:",9)==0)
|
||
|
{
|
||
|
sizeServerStr = QCString(line+9);
|
||
|
sizeServer = sizeServerStr.toULong();
|
||
|
lastStr = &sizeServerStr;
|
||
|
}
|
||
|
else if (strncasecmp(line,"X-UID:",6)==0)
|
||
|
{
|
||
|
uidStr = QCString(line+6);
|
||
|
uid = uidStr.toULong();
|
||
|
lastStr = &uidStr;
|
||
|
}
|
||
|
else if (strncasecmp(line, "Content-Type:", 13) == 0)
|
||
|
{
|
||
|
contentTypeStr = QCString(line+13);
|
||
|
lastStr = &contentTypeStr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mAutoCreateIndex)
|
||
|
{
|
||
|
emit statusMsg(i18n("Writing index file"));
|
||
|
writeIndex();
|
||
|
}
|
||
|
else mHeaderOffset = 0;
|
||
|
|
||
|
correctUnreadMsgsCount();
|
||
|
|
||
|
if (kmkernel->outboxFolder() == folder() && count() > 0)
|
||
|
KMessageBox::queuedMessageBox(0, KMessageBox::Information,
|
||
|
i18n("Your outbox contains messages which were "
|
||
|
"most-likely not created by KMail;\nplease remove them from there if you "
|
||
|
"do not want KMail to send them."));
|
||
|
|
||
|
invalidateFolder();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
KMMessage* KMFolderMbox::readMsg(int idx)
|
||
|
{
|
||
|
KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
|
||
|
|
||
|
assert(mi!=0 && !mi->isMessage());
|
||
|
assert(mStream != 0);
|
||
|
|
||
|
KMMessage *msg = new KMMessage(*mi);
|
||
|
msg->setMsgInfo( mi ); // remember the KMMsgInfo object to that we can restore it when the KMMessage object is no longer needed
|
||
|
mMsgList.set(idx,&msg->toMsgBase()); // done now so that the serial number can be computed
|
||
|
msg->fromDwString(getDwString(idx));
|
||
|
return msg;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define STRDIM(x) (sizeof(x)/sizeof(*x)-1)
|
||
|
// performs (\n|^)>{n}From_ -> \1>{n-1}From_ conversion
|
||
|
static size_t unescapeFrom( char* str, size_t strLen ) {
|
||
|
if ( !str )
|
||
|
return 0;
|
||
|
if ( strLen <= STRDIM(">From ") )
|
||
|
return strLen;
|
||
|
|
||
|
// yes, *d++ = *s++ is a no-op as long as d == s (until after the
|
||
|
// first >From_), but writes are cheap compared to reads and the
|
||
|
// data is already in the cache from the read, so special-casing
|
||
|
// might even be slower...
|
||
|
const char * s = str;
|
||
|
char * d = str;
|
||
|
const char * const e = str + strLen - STRDIM(">From ");
|
||
|
|
||
|
while ( s < e ) {
|
||
|
if ( *s == '\n' && *(s+1) == '>' ) { // we can do the lookahead, since e is 6 chars from the end!
|
||
|
*d++ = *s++; // == '\n'
|
||
|
*d++ = *s++; // == '>'
|
||
|
while ( s < e && *s == '>' )
|
||
|
*d++ = *s++;
|
||
|
if ( qstrncmp( s, "From ", STRDIM("From ") ) == 0 )
|
||
|
--d;
|
||
|
}
|
||
|
*d++ = *s++; // yes, s might be e here, but e is not the end :-)
|
||
|
}
|
||
|
// copy the rest:
|
||
|
while ( s < str + strLen )
|
||
|
*d++ = *s++;
|
||
|
if ( d < s ) // only NUL-terminate if it's shorter
|
||
|
*d = 0;
|
||
|
|
||
|
return d - str;
|
||
|
}
|
||
|
|
||
|
//static
|
||
|
QByteArray KMFolderMbox::escapeFrom( const DwString & str ) {
|
||
|
const unsigned int strLen = str.length();
|
||
|
if ( strLen <= STRDIM("From ") )
|
||
|
return KMail::Util::ByteArray( str );
|
||
|
// worst case: \nFrom_\nFrom_\nFrom_... => grows to 7/6
|
||
|
QByteArray result( int( strLen + 5 ) / 6 * 7 + 1 );
|
||
|
|
||
|
const char * s = str.data();
|
||
|
const char * const e = s + strLen - STRDIM("From ");
|
||
|
char * d = result.data();
|
||
|
|
||
|
bool onlyAnglesAfterLF = false; // dont' match ^From_
|
||
|
while ( s < e ) {
|
||
|
switch ( *s ) {
|
||
|
case '\n':
|
||
|
onlyAnglesAfterLF = true;
|
||
|
break;
|
||
|
case '>':
|
||
|
break;
|
||
|
case 'F':
|
||
|
if ( onlyAnglesAfterLF && qstrncmp( s+1, "rom ", STRDIM("rom ") ) == 0 )
|
||
|
*d++ = '>';
|
||
|
// fall through
|
||
|
default:
|
||
|
onlyAnglesAfterLF = false;
|
||
|
break;
|
||
|
}
|
||
|
*d++ = *s++;
|
||
|
}
|
||
|
while ( s < str.data() + strLen )
|
||
|
*d++ = *s++;
|
||
|
|
||
|
result.truncate( d - result.data() );
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#undef STRDIM
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
DwString KMFolderMbox::getDwString(int idx)
|
||
|
{
|
||
|
KMMsgInfo* mi = (KMMsgInfo*)mMsgList[idx];
|
||
|
|
||
|
assert(mi!=0);
|
||
|
assert(mStream != 0);
|
||
|
|
||
|
size_t msgSize = mi->msgSize();
|
||
|
char* msgText = new char[ msgSize + 1 ];
|
||
|
|
||
|
fseek(mStream, mi->folderOffset(), SEEK_SET);
|
||
|
fread(msgText, msgSize, 1, mStream);
|
||
|
msgText[msgSize] = '\0';
|
||
|
|
||
|
size_t newMsgSize = unescapeFrom( msgText, msgSize );
|
||
|
newMsgSize = KMail::Util::crlf2lf( msgText, newMsgSize );
|
||
|
|
||
|
DwString msgStr;
|
||
|
// the DwString takes possession of msgText, so we must not delete msgText
|
||
|
msgStr.TakeBuffer( msgText, msgSize + 1, 0, newMsgSize );
|
||
|
return msgStr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::addMsg( KMMessage* aMsg, int* aIndex_ret )
|
||
|
{
|
||
|
if (!canAddMsgNow(aMsg, aIndex_ret)) return 0;
|
||
|
QByteArray msgText;
|
||
|
char endStr[3];
|
||
|
int idx = -1, rc;
|
||
|
KMFolder* msgParent;
|
||
|
bool editing = false;
|
||
|
int growth = 0;
|
||
|
|
||
|
KMFolderOpener openThis(folder(), "mboxaddMsg");
|
||
|
rc = openThis.openResult();
|
||
|
if (rc)
|
||
|
{
|
||
|
kdDebug(5006) << "KMFolderMbox::addMsg-open: " << rc << " of folder: " << label() << endl;
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
// take message out of the folder it is currently in, if any
|
||
|
msgParent = aMsg->parent();
|
||
|
if (msgParent)
|
||
|
{
|
||
|
if ( msgParent== folder() )
|
||
|
{
|
||
|
if (kmkernel->folderIsDraftOrOutbox( folder() ))
|
||
|
//special case for Edit message.
|
||
|
{
|
||
|
kdDebug(5006) << "Editing message in outbox or drafts" << endl;
|
||
|
editing = true;
|
||
|
}
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
idx = msgParent->find(aMsg);
|
||
|
msgParent->getMsg( idx );
|
||
|
}
|
||
|
|
||
|
if (folderType() != KMFolderTypeImap)
|
||
|
{
|
||
|
/*
|
||
|
QFile fileD0( "testdat_xx-kmfoldermbox-0" );
|
||
|
if( fileD0.open( IO_WriteOnly ) ) {
|
||
|
QDataStream ds( &fileD0 );
|
||
|
ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
|
||
|
fileD0.close(); // If data is 0 we just create a zero length file.
|
||
|
}
|
||
|
*/
|
||
|
aMsg->setStatusFields();
|
||
|
/*
|
||
|
QFile fileD1( "testdat_xx-kmfoldermbox-1" );
|
||
|
if( fileD1.open( IO_WriteOnly ) ) {
|
||
|
QDataStream ds( &fileD1 );
|
||
|
ds.writeRawBytes( aMsg->asString(), aMsg->asString().length() );
|
||
|
fileD1.close(); // If data is 0 we just create a zero length file.
|
||
|
}
|
||
|
*/
|
||
|
if (aMsg->headerField("Content-Type").isEmpty()) // This might be added by
|
||
|
aMsg->removeHeaderField("Content-Type"); // the line above
|
||
|
}
|
||
|
msgText = escapeFrom( aMsg->asDwString() );
|
||
|
size_t len = msgText.size();
|
||
|
|
||
|
assert(mStream != 0);
|
||
|
clearerr(mStream);
|
||
|
if (len <= 0)
|
||
|
{
|
||
|
kdDebug(5006) << "Message added to folder `" << name() << "' contains no data. Ignoring it." << endl;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Make sure the file is large enough to check for an end
|
||
|
// character
|
||
|
fseek(mStream, 0, SEEK_END);
|
||
|
off_t revert = ftell(mStream);
|
||
|
if (ftell(mStream) >= 2) {
|
||
|
// write message to folder file
|
||
|
fseek(mStream, -2, SEEK_END);
|
||
|
fread(endStr, 1, 2, mStream); // ensure separating empty line
|
||
|
if (ftell(mStream) > 0 && endStr[0]!='\n') {
|
||
|
++growth;
|
||
|
if (endStr[1]!='\n') {
|
||
|
//printf ("****endStr[1]=%c\n", endStr[1]);
|
||
|
fwrite("\n\n", 1, 2, mStream);
|
||
|
++growth;
|
||
|
}
|
||
|
else fwrite("\n", 1, 1, mStream);
|
||
|
}
|
||
|
}
|
||
|
fseek(mStream,0,SEEK_END); // this is needed on solaris and others
|
||
|
int error = ferror(mStream);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
QCString messageSeparator( aMsg->mboxMessageSeparator() );
|
||
|
fwrite( messageSeparator.data(), messageSeparator.length(), 1, mStream );
|
||
|
off_t offs = ftell(mStream);
|
||
|
fwrite(msgText.data(), len, 1, mStream);
|
||
|
if (msgText[(int)len-1]!='\n') fwrite("\n\n", 1, 2, mStream);
|
||
|
fflush(mStream);
|
||
|
size_t size = ftell(mStream) - offs;
|
||
|
|
||
|
error = ferror(mStream);
|
||
|
if (error) {
|
||
|
kdDebug(5006) << "Error: Could not add message to folder: " << strerror(errno) << endl;
|
||
|
if (ftell(mStream) > revert) {
|
||
|
kdDebug(5006) << "Undoing changes" << endl;
|
||
|
truncate( QFile::encodeName(location()), revert );
|
||
|
}
|
||
|
kmkernel->emergencyExit( i18n("Could not add message to folder: ") + QString::fromLocal8Bit(strerror(errno)));
|
||
|
|
||
|
/* This code is not 100% reliable
|
||
|
bool busy = kmkernel->kbp()->isBusy();
|
||
|
if (busy) kmkernel->kbp()->idle();
|
||
|
KMessageBox::sorry(0,
|
||
|
i18n("Unable to add message to folder.\n"
|
||
|
"(No space left on device or insufficient quota?)\n"
|
||
|
"Free space and sufficient quota are required to continue safely."));
|
||
|
if (busy) kmkernel->kbp()->busy();
|
||
|
kmkernel->kbp()->idle();
|
||
|
*/
|
||
|
return error;
|
||
|
}
|
||
|
|
||
|
if (msgParent) {
|
||
|
if (idx >= 0) msgParent->take(idx);
|
||
|
}
|
||
|
// if (mAccount) aMsg->removeHeaderField("X-UID");
|
||
|
|
||
|
if (aMsg->isUnread() || aMsg->isNew() ||
|
||
|
(folder() == kmkernel->outboxFolder())) {
|
||
|
if (mUnreadMsgs == -1) mUnreadMsgs = 1;
|
||
|
else ++mUnreadMsgs;
|
||
|
if ( !mQuiet )
|
||
|
emit numUnreadMsgsChanged( folder() );
|
||
|
}
|
||
|
++mTotalMsgs;
|
||
|
mSize = -1;
|
||
|
|
||
|
if ( aMsg->attachmentState() == KMMsgAttachmentUnknown &&
|
||
|
aMsg->readyToShow() )
|
||
|
aMsg->updateAttachmentState();
|
||
|
|
||
|
// store information about the position in the folder file in the message
|
||
|
aMsg->setParent(folder());
|
||
|
aMsg->setFolderOffset(offs);
|
||
|
aMsg->setMsgSize(size);
|
||
|
idx = mMsgList.append(&aMsg->toMsgBase(), mExportsSernums );
|
||
|
if ( aMsg->getMsgSerNum() <= 0 )
|
||
|
aMsg->setMsgSerNum();
|
||
|
else
|
||
|
replaceMsgSerNum( aMsg->getMsgSerNum(), &aMsg->toMsgBase(), idx );
|
||
|
|
||
|
// change the length of the previous message to encompass white space added
|
||
|
if ((idx > 0) && (growth > 0)) {
|
||
|
// don't grow if a deleted message claims space at the end of the file
|
||
|
if ((ulong)revert == mMsgList[idx - 1]->folderOffset() + mMsgList[idx - 1]->msgSize() )
|
||
|
mMsgList[idx - 1]->setMsgSize( mMsgList[idx - 1]->msgSize() + growth );
|
||
|
}
|
||
|
|
||
|
// write index entry if desired
|
||
|
if (mAutoCreateIndex)
|
||
|
{
|
||
|
assert(mIndexStream != 0);
|
||
|
clearerr(mIndexStream);
|
||
|
fseek(mIndexStream, 0, SEEK_END);
|
||
|
revert = ftell(mIndexStream);
|
||
|
|
||
|
KMMsgBase * mb = &aMsg->toMsgBase();
|
||
|
int len;
|
||
|
const uchar *buffer = mb->asIndexString(len);
|
||
|
fwrite(&len,sizeof(len), 1, mIndexStream);
|
||
|
mb->setIndexOffset( ftell(mIndexStream) );
|
||
|
mb->setIndexLength( len );
|
||
|
if(fwrite(buffer, len, 1, mIndexStream) != 1)
|
||
|
kdDebug(5006) << "Whoa! " << __FILE__ << ":" << __LINE__ << endl;
|
||
|
|
||
|
fflush(mIndexStream);
|
||
|
error = ferror(mIndexStream);
|
||
|
|
||
|
if ( mExportsSernums )
|
||
|
error |= appendToFolderIdsFile( idx );
|
||
|
|
||
|
if (error) {
|
||
|
kdWarning(5006) << "Error: Could not add message to folder (No space left on device?)" << endl;
|
||
|
if (ftell(mIndexStream) > revert) {
|
||
|
kdWarning(5006) << "Undoing changes" << endl;
|
||
|
truncate( QFile::encodeName(indexLocation()), revert );
|
||
|
}
|
||
|
if ( errno )
|
||
|
kmkernel->emergencyExit( i18n("Could not add message to folder:") + QString::fromLocal8Bit(strerror(errno)));
|
||
|
else
|
||
|
kmkernel->emergencyExit( i18n("Could not add message to folder (No space left on device?)") );
|
||
|
|
||
|
/* This code may not be 100% reliable
|
||
|
bool busy = kmkernel->kbp()->isBusy();
|
||
|
if (busy) kmkernel->kbp()->idle();
|
||
|
KMessageBox::sorry(0,
|
||
|
i18n("Unable to add message to folder.\n"
|
||
|
"(No space left on device or insufficient quota?)\n"
|
||
|
"Free space and sufficient quota are required to continue safely."));
|
||
|
if (busy) kmkernel->kbp()->busy();
|
||
|
*/
|
||
|
return error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (aIndex_ret) *aIndex_ret = idx;
|
||
|
emitMsgAddedSignals(idx);
|
||
|
|
||
|
// All streams have been flushed without errors if we arrive here
|
||
|
// Return success!
|
||
|
// (Don't return status of stream, it may have been closed already.)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int KMFolderMbox::compact( unsigned int startIndex, int nbMessages, FILE* tmpfile, off_t& offs, bool& done )
|
||
|
{
|
||
|
int rc = 0;
|
||
|
QCString mtext;
|
||
|
unsigned int stopIndex = nbMessages == -1 ? mMsgList.count() :
|
||
|
QMIN( mMsgList.count(), startIndex + nbMessages );
|
||
|
//kdDebug(5006) << "KMFolderMbox: compacting from " << startIndex << " to " << stopIndex << endl;
|
||
|
for(unsigned int idx = startIndex; idx < stopIndex; ++idx) {
|
||
|
KMMsgInfo* mi = (KMMsgInfo*)mMsgList.at(idx);
|
||
|
size_t msize = mi->msgSize();
|
||
|
if (mtext.size() < msize + 2)
|
||
|
mtext.resize(msize+2);
|
||
|
off_t folder_offset = mi->folderOffset();
|
||
|
|
||
|
//now we need to find the separator! grr...
|
||
|
for(off_t i = folder_offset-25; true; i -= 20) {
|
||
|
off_t chunk_offset = i <= 0 ? 0 : i;
|
||
|
if(fseek(mStream, chunk_offset, SEEK_SET) == -1) {
|
||
|
rc = errno;
|
||
|
break;
|
||
|
}
|
||
|
if (mtext.size() < 20)
|
||
|
mtext.resize(20);
|
||
|
fread(mtext.data(), 20, 1, mStream);
|
||
|
if(i <= 0) { //woops we've reached the top of the file, last try..
|
||
|
if ( mtext.contains( "from ", false ) ) {
|
||
|
if (mtext.size() < (size_t)folder_offset)
|
||
|
mtext.resize(folder_offset);
|
||
|
if(fseek(mStream, chunk_offset, SEEK_SET) == -1 ||
|
||
|
!fread(mtext.data(), folder_offset, 1, mStream) ||
|
||
|
!fwrite(mtext.data(), folder_offset, 1, tmpfile)) {
|
||
|
rc = errno;
|
||
|
break;
|
||
|
}
|
||
|
offs += folder_offset;
|
||
|
} else {
|
||
|
rc = 666;
|
||
|
}
|
||
|
break;
|
||
|
} else {
|
||
|
int last_crlf = -1;
|
||
|
for(int i2 = 0; i2 < 20; i2++) {
|
||
|
if(*(mtext.data()+i2) == '\n')
|
||
|
last_crlf = i2;
|
||
|
}
|
||
|
if(last_crlf != -1) {
|
||
|
int size = folder_offset - (i + last_crlf+1);
|
||
|
if ((int)mtext.size() < size)
|
||
|
mtext.resize(size);
|
||
|
if(fseek(mStream, i + last_crlf+1, SEEK_SET) == -1 ||
|
||
|
!fread(mtext.data(), size, 1, mStream) ||
|
||
|
!fwrite(mtext.data(), size, 1, tmpfile)) {
|
||
|
rc = errno;
|
||
|
break;
|
||
|
}
|
||
|
offs += size;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (rc)
|
||
|
break;
|
||
|
|
||
|
//now actually write the message
|
||
|
if(fseek(mStream, folder_offset, SEEK_SET) == -1 ||
|
||
|
!fread(mtext.data(), msize, 1, mStream) || !fwrite(mtext.data(), msize, 1, tmpfile)) {
|
||
|
rc = errno;
|
||
|
break;
|
||
|
}
|
||
|
mi->setFolderOffset(offs);
|
||
|
offs += msize;
|
||
|
}
|
||
|
done = ( !rc && stopIndex == mMsgList.count() ); // finished without errors
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::compact( bool silent )
|
||
|
{
|
||
|
// This is called only when the user explicitely requests compaction,
|
||
|
// so we don't check needsCompact.
|
||
|
|
||
|
KMail::MboxCompactionJob* job = new KMail::MboxCompactionJob( folder(), true /*immediate*/ );
|
||
|
int rc = job->executeNow( silent );
|
||
|
// Note that job autodeletes itself.
|
||
|
|
||
|
// If this is the current folder, the changed signal will ultimately call
|
||
|
// KMHeaders::setFolderInfoStatus which will override the message, so save/restore it
|
||
|
QString statusMsg = BroadcastStatus::instance()->statusMsg();
|
||
|
emit changed();
|
||
|
BroadcastStatus::instance()->setStatusMsg( statusMsg );
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void KMFolderMbox::setLockType( LockType ltype )
|
||
|
{
|
||
|
mLockType = ltype;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void KMFolderMbox::setProcmailLockFileName( const QString &fname )
|
||
|
{
|
||
|
mProcmailLockFileName = fname;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::removeContents()
|
||
|
{
|
||
|
int rc = 0;
|
||
|
rc = unlink(QFile::encodeName(location()));
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int KMFolderMbox::expungeContents()
|
||
|
{
|
||
|
int rc = 0;
|
||
|
if (truncate(QFile::encodeName(location()), 0))
|
||
|
rc = errno;
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
/*virtual*/
|
||
|
Q_INT64 KMFolderMbox::doFolderSize() const
|
||
|
{
|
||
|
QFileInfo info( location() );
|
||
|
return (Q_INT64)(info.size());
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
#include "kmfoldermbox.moc"
|