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.
 
 
 

808 lines
20 KiB

// -*- mode: C++; c-file-style: "gnu" -*-
// kmfolder.cpp
// Author: Stefan Taferner <taferner@alpin.or.at>
#include <config.h>
#include "kmfolder.h"
#include "kmfolderdir.h"
#include "kmfoldermbox.h"
#include "folderstorage.h"
#include "kmfoldercachedimap.h"
#include "kmfoldersearch.h"
#include "kmfolderimap.h"
#include "kmfoldermgr.h"
#include <libkpimidentities/identitymanager.h>
#include <libkpimidentities/identity.h>
#include "expirejob.h"
#include "compactionjob.h"
#include "kmfoldertree.h"
#include <errno.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <qfile.h>
KMFolder::KMFolder( KMFolderDir* aParent, const QString& aFolderName,
KMFolderType aFolderType )
: KMFolderNode( aParent, aFolderName ), mStorage(0),
mParent( aParent ), mChild( 0 ),
mIsSystemFolder( false ),
mExpireMessages( false ), mUnreadExpireAge( 28 ),
mReadExpireAge( 14 ), mUnreadExpireUnits( expireNever ),
mReadExpireUnits( expireNever ),
mExpireAction( ExpireDelete ),
mUseCustomIcons( false ), mMailingListEnabled( false )
{
if( aFolderType == KMFolderTypeCachedImap )
mStorage = new KMFolderCachedImap( this, aFolderName.latin1() );
else if( aFolderType == KMFolderTypeImap )
mStorage = new KMFolderImap( this, aFolderName.latin1() );
else if( aFolderType == KMFolderTypeMaildir )
mStorage = new KMFolderMaildir( this, aFolderName.latin1() );
else if( aFolderType == KMFolderTypeSearch )
mStorage = new KMFolderSearch( this, aFolderName.latin1() );
else
mStorage = new KMFolderMbox( this, aFolderName.latin1() );
if ( aParent ) {
connect( mStorage, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
aParent->manager(), SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ) );
connect( mStorage, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
parent()->manager(), SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ) );
connect( this, SIGNAL( msgChanged( KMFolder*, Q_UINT32, int ) ),
parent()->manager(), SIGNAL( msgChanged( KMFolder*, Q_UINT32, int ) ) );
connect( this, SIGNAL( msgHeaderChanged( KMFolder*, int ) ),
parent()->manager(), SIGNAL( msgHeaderChanged( KMFolder*, int ) ) );
}
// Resend all mStorage signals
connect( mStorage, SIGNAL( changed() ), SIGNAL( changed() ) );
connect( mStorage, SIGNAL( cleared() ), SIGNAL( cleared() ) );
connect( mStorage, SIGNAL( expunged( KMFolder* ) ),
SIGNAL( expunged( KMFolder* ) ) );
connect( mStorage, SIGNAL( nameChanged() ), SIGNAL( nameChanged() ) );
connect( mStorage, SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ),
SIGNAL( msgRemoved( KMFolder*, Q_UINT32 ) ) );
connect( mStorage, SIGNAL( msgRemoved( int, QString, QString ) ),
SIGNAL( msgRemoved( int, QString, QString ) ) );
connect( mStorage, SIGNAL( msgRemoved( KMFolder* ) ),
SIGNAL( msgRemoved( KMFolder* ) ) );
connect( mStorage, SIGNAL( msgAdded( int ) ), SIGNAL( msgAdded( int ) ) );
connect( mStorage, SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ),
SIGNAL( msgAdded( KMFolder*, Q_UINT32 ) ) );
connect( mStorage, SIGNAL( msgChanged( KMFolder*, Q_UINT32 , int ) ),
SIGNAL( msgChanged( KMFolder*, Q_UINT32 , int ) ) );
connect( mStorage, SIGNAL( msgHeaderChanged( KMFolder*, int ) ),
SIGNAL( msgHeaderChanged( KMFolder*, int ) ) );
connect( mStorage, SIGNAL( statusMsg( const QString& ) ),
SIGNAL( statusMsg( const QString& ) ) );
connect( mStorage, SIGNAL( numUnreadMsgsChanged( KMFolder* ) ),
SIGNAL( numUnreadMsgsChanged( KMFolder* ) ) );
connect( mStorage, SIGNAL( removed( KMFolder*, bool ) ),
SIGNAL( removed( KMFolder*, bool ) ) );
//FIXME: Centralize all the readConfig calls somehow - Zack
mStorage->readConfig();
if ( mId == 0 && aParent )
mId = aParent->manager()->createId();
}
KMFolder::~KMFolder()
{
delete mStorage;
}
void KMFolder::readConfig( KConfig* config )
{
if ( !config->readEntry("SystemLabel").isEmpty() )
mSystemLabel = config->readEntry("SystemLabel");
mExpireMessages = config->readBoolEntry("ExpireMessages", false);
mReadExpireAge = config->readNumEntry("ReadExpireAge", 3);
mReadExpireUnits = (ExpireUnits)config->readNumEntry("ReadExpireUnits", expireMonths);
mUnreadExpireAge = config->readNumEntry("UnreadExpireAge", 12);
mUnreadExpireUnits = (ExpireUnits)config->readNumEntry("UnreadExpireUnits", expireNever);
mExpireAction = config->readEntry("ExpireAction", "Delete") == "Move" ? ExpireMove : ExpireDelete;
mExpireToFolderId = config->readEntry("ExpireToFolder");
mUseCustomIcons = config->readBoolEntry("UseCustomIcons", false );
mNormalIconPath = config->readEntry("NormalIconPath" );
mUnreadIconPath = config->readEntry("UnreadIconPath" );
mMailingListEnabled = config->readBoolEntry("MailingListEnabled");
mMailingList.readConfig( config );
mIdentity = config->readUnsignedNumEntry("Identity",0);
setUserWhoField( config->readEntry("WhoField"), false );
uint savedId = config->readUnsignedNumEntry("Id", 0);
// make sure that we don't overwrite a valid id
if ( savedId != 0 && mId == 0 )
mId = savedId;
mPutRepliesInSameFolder = config->readBoolEntry( "PutRepliesInSameFolder", false );
mIgnoreNewMail = config->readBoolEntry( "IgnoreNewMail", false );
if ( mUseCustomIcons )
emit iconsChanged();
}
void KMFolder::writeConfig( KConfig* config ) const
{
config->writeEntry("SystemLabel", mSystemLabel);
config->writeEntry("ExpireMessages", mExpireMessages);
config->writeEntry("ReadExpireAge", mReadExpireAge);
config->writeEntry("ReadExpireUnits", mReadExpireUnits);
config->writeEntry("UnreadExpireAge", mUnreadExpireAge);
config->writeEntry("UnreadExpireUnits", mUnreadExpireUnits);
config->writeEntry("ExpireAction", mExpireAction == ExpireDelete ? "Delete" : "Move");
config->writeEntry("ExpireToFolder", mExpireToFolderId);
config->writeEntry("UseCustomIcons", mUseCustomIcons);
config->writeEntry("NormalIconPath", mNormalIconPath);
config->writeEntry("UnreadIconPath", mUnreadIconPath);
config->writeEntry("MailingListEnabled", mMailingListEnabled);
mMailingList.writeConfig( config );
config->writeEntry("Identity", mIdentity);
config->writeEntry("WhoField", mUserWhoField);
config->writeEntry("Id", mId);
config->writeEntry( "PutRepliesInSameFolder", mPutRepliesInSameFolder );
config->writeEntry( "IgnoreNewMail", mIgnoreNewMail );
}
KMFolderType KMFolder::folderType() const
{
return mStorage ? mStorage->folderType() : KMFolderTypeUnknown;
}
QString KMFolder::fileName() const
{
return mStorage ? mStorage->fileName() : QString::null;
}
QString KMFolder::location() const
{
return mStorage ? mStorage->location() : QString::null;
}
QString KMFolder::indexLocation() const
{
return mStorage ? mStorage->indexLocation() : QString::null;
}
QString KMFolder::subdirLocation() const
{
QString sLocation( path() );
if( !sLocation.isEmpty() )
sLocation += '/';
sLocation += '.' + FolderStorage::dotEscape( fileName() ) + ".directory";
return sLocation;
}
KMFolderDir* KMFolder::createChildFolder()
{
if( mChild )
return mChild;
QString childName = "." + fileName() + ".directory";
QString childDir = path() + "/" + childName;
if (access(QFile::encodeName(childDir), W_OK) != 0) // Not there or not writable
{
if (mkdir(QFile::encodeName(childDir), S_IRWXU) != 0
&& chmod(QFile::encodeName(childDir), S_IRWXU) != 0) {
QString wmsg = QString(" '%1': %2").arg(childDir).arg(strerror(errno));
KMessageBox::information(0,i18n("Failed to create folder") + wmsg);
return 0;
}
}
mChild = new KMFolderDir( this, parent(), childName,
(folderType() == KMFolderTypeImap) ? KMImapDir : KMStandardDir);
if( !mChild )
return 0;
mChild->reload();
parent()->append( mChild );
return mChild;
}
void KMFolder::setChild( KMFolderDir* aChild )
{
mChild = aChild;
mStorage->updateChildrenState();
}
bool KMFolder::noContent() const
{
return mStorage->noContent();
}
void KMFolder::setNoContent( bool aNoContent )
{
mStorage->setNoContent( aNoContent );
}
bool KMFolder::noChildren() const
{
return mStorage->noChildren();
}
void KMFolder::setNoChildren( bool aNoChildren )
{
mStorage->setNoChildren( aNoChildren );
}
KMMessage* KMFolder::getMsg( int idx )
{
return mStorage->getMsg( idx );
}
KMMsgInfo* KMFolder::unGetMsg( int idx )
{
return mStorage->unGetMsg( idx );
}
bool KMFolder::isMessage( int idx )
{
return mStorage->isMessage( idx );
}
QCString& KMFolder::getMsgString( int idx, QCString& mDest )
{
return mStorage->getMsgString( idx, mDest );
}
DwString KMFolder::getDwString( int idx )
{
return mStorage->getDwString( idx );
}
void KMFolder::ignoreJobsForMessage( KMMessage* m )
{
mStorage->ignoreJobsForMessage( m );
}
FolderJob* KMFolder::createJob( KMMessage *msg, FolderJob::JobType jt,
KMFolder *folder, QString partSpecifier,
const AttachmentStrategy *as ) const
{
return mStorage->createJob( msg, jt, folder, partSpecifier, as );
}
FolderJob* KMFolder::createJob( QPtrList<KMMessage>& msgList,
const QString& sets,
FolderJob::JobType jt, KMFolder *folder ) const
{
return mStorage->createJob( msgList, sets, jt, folder );
}
const KMMsgBase* KMFolder::getMsgBase( int idx ) const
{
return mStorage->getMsgBase( idx );
}
KMMsgBase* KMFolder::getMsgBase( int idx )
{
return mStorage->getMsgBase( idx );
}
const KMMsgBase* KMFolder::operator[]( int idx ) const
{
return mStorage->operator[]( idx );
}
KMMsgBase* KMFolder::operator[]( int idx )
{
return mStorage->operator[]( idx );
}
KMMessage* KMFolder::take( int idx )
{
return mStorage->take( idx );
}
void KMFolder::take( QPtrList<KMMessage> msgList )
{
mStorage->take( msgList );
}
int KMFolder::addMsg( KMMessage* msg, int* index_return )
{
return mStorage->addMsg( msg, index_return );
}
int KMFolder::addMsgKeepUID( KMMessage* msg, int* index_return )
{
return mStorage->addMsgKeepUID( msg, index_return );
}
void KMFolder::emitMsgAddedSignals( int idx )
{
mStorage->emitMsgAddedSignals( idx );
}
void KMFolder::removeMsg( int i, bool imapQuiet )
{
mStorage->removeMsg( i, imapQuiet );
}
void KMFolder::removeMsg( QPtrList<KMMessage> msgList, bool imapQuiet )
{
mStorage->removeMsg( msgList, imapQuiet );
}
int KMFolder::expungeOldMsg( int days )
{
return mStorage->expungeOldMsg( days );
}
int KMFolder::moveMsg( KMMessage* msg, int* index_return )
{
return mStorage->moveMsg( msg, index_return );
}
int KMFolder::moveMsg(QPtrList<KMMessage> q, int* index_return )
{
return mStorage->moveMsg( q, index_return );
}
int KMFolder::find( const KMMsgBase* msg ) const
{
return mStorage->find( msg );
}
int KMFolder::find( const KMMessage* msg ) const
{
return mStorage->find( msg );
}
int KMFolder::count( bool cache ) const
{
return mStorage->count( cache );
}
int KMFolder::countUnread()
{
return mStorage->countUnread();
}
int KMFolder::countUnreadRecursive()
{
KMFolder *folder;
int count = countUnread();
KMFolderDir *dir = child();
if (!dir)
return count;
QPtrListIterator<KMFolderNode> it(*dir);
for ( ; it.current(); ++it )
if (!it.current()->isDir()) {
folder = static_cast<KMFolder*>(it.current());
count += folder->countUnreadRecursive();
}
return count;
}
void KMFolder::msgStatusChanged( const KMMsgStatus oldStatus,
const KMMsgStatus newStatus, int idx )
{
mStorage->msgStatusChanged( oldStatus, newStatus, idx );
}
int KMFolder::open()
{
return mStorage->open();
}
int KMFolder::canAccess()
{
return mStorage->canAccess();
}
void KMFolder::close( bool force )
{
mStorage->close( force );
}
void KMFolder::sync()
{
mStorage->sync();
}
bool KMFolder::isOpened() const
{
return mStorage->isOpened();
}
void KMFolder::markNewAsUnread()
{
mStorage->markNewAsUnread();
}
void KMFolder::markUnreadAsRead()
{
mStorage->markUnreadAsRead();
}
int KMFolder::create( bool imap )
{
return mStorage->create( imap );
}
void KMFolder::remove()
{
mStorage->remove();
}
int KMFolder::expunge()
{
return mStorage->expunge();
}
int KMFolder::rename( const QString& newName, KMFolderDir *aParent )
{
return mStorage->rename( newName, aParent );
}
bool KMFolder::autoCreateIndex() const
{
return mStorage->autoCreateIndex();
}
void KMFolder::setAutoCreateIndex( bool b )
{
mStorage->setAutoCreateIndex( b );
}
bool KMFolder::dirty() const
{
return mStorage->dirty();
}
void KMFolder::setDirty( bool f )
{
mStorage->setDirty( f );
}
bool KMFolder::needsCompacting() const
{
return mStorage->needsCompacting();
}
void KMFolder::setNeedsCompacting( bool f )
{
mStorage->setNeedsCompacting( f );
}
void KMFolder::quiet( bool beQuiet )
{
mStorage->quiet( beQuiet );
}
bool KMFolder::isReadOnly() const
{
return mStorage->isReadOnly();
}
QString KMFolder::label() const
{
if ( !mSystemLabel.isEmpty() )
return mSystemLabel;
if ( !mLabel.isEmpty() )
return mLabel;
if ( isSystemFolder() )
return i18n( name().latin1() );
return name();
}
//-----------------------------------------------------------------------------
QString KMFolder::prettyURL() const
{
if ( parent() )
return parent()->prettyURL() + "/" + label();
else
return "/" + label();
}
//--------------------------------------------------------------------------
QString KMFolder::mailingListPostAddress() const
{
if ( mMailingList.features() & MailingList::Post ) {
KURL::List::const_iterator it;
KURL::List post = mMailingList.postURLS();
for( it = post.begin(); it != post.end(); ++it ) {
// We check for isEmpty because before 3.3 postAddress was just an
// email@kde.org and that leaves protocol() field in the kurl class
if ( (*it).protocol() == "mailto" || (*it).protocol().isEmpty() )
return (*it).path();
}
}
return QString::null;
}
const char* KMFolder::type() const
{
return mStorage->type();
}
bool KMFolder::hasAccounts() const
{
return mStorage->hasAccounts();
}
void KMFolder::setMailingListEnabled( bool enabled )
{
mMailingListEnabled = enabled;
mStorage->writeConfig();
}
void KMFolder::setMailingList( const MailingList& mlist )
{
mMailingList = mlist;
mStorage->writeConfig();
}
void KMFolder::setIdentity( uint identity )
{
mIdentity = identity;
kmkernel->slotRequestConfigSync();
}
void KMFolder::setWhoField(const QString& aWhoField )
{
mWhoField = aWhoField;
#if 0
// This isn't saved in the config anyway
mStorage->writeConfig();
#endif
}
void KMFolder::setUserWhoField( const QString& whoField, bool writeConfig )
{
mUserWhoField = whoField;
if ( whoField.isEmpty() )
{
// default setting
const KPIM::Identity & identity =
kmkernel->identityManager()->identityForUoidOrDefault( mIdentity );
if ( isSystemFolder() && folderType() != KMFolderTypeImap ) {
// local system folders
if ( this == kmkernel->inboxFolder() ||
this == kmkernel->trashFolder() )
mWhoField = "From";
if ( this == kmkernel->outboxFolder() ||
this == kmkernel->sentFolder() ||
this == kmkernel->draftsFolder() )
mWhoField = "To";
} else if ( identity.drafts() == idString()
|| identity.fcc() == idString() )
// drafts or sent of the identity
mWhoField = "To";
else
mWhoField = "From";
} else if ( whoField == "From" || whoField == "To" )
// set the whoField according to the user-setting
mWhoField = whoField;
else
// this should not happen...
kdDebug(5006) << "Illegal setting " << whoField << " for userWhoField!"
<< endl;
if (writeConfig)
mStorage->writeConfig();
}
void KMFolder::correctUnreadMsgsCount()
{
mStorage->correctUnreadMsgsCount();
}
QString KMFolder::idString() const
{
KMFolderNode* folderNode = parent();
if (!folderNode)
return "";
while ( folderNode->parent() )
folderNode = folderNode->parent();
QString myPath = path();
int pathLen = myPath.length() - folderNode->path().length();
QString relativePath = myPath.right( pathLen );
if (!relativePath.isEmpty())
relativePath = relativePath.right( relativePath.length() - 1 ) + "/";
QString escapedName = name();
/* Escape [ and ] as they are disallowed for kconfig sections and that is
what the idString is primarily used for. */
escapedName.replace( "[", "%(" );
escapedName.replace( "]", "%)" );
return relativePath + escapedName;
}
void KMFolder::setAutoExpire( bool enabled )
{
if( enabled != mExpireMessages ) {
mExpireMessages = enabled;
mStorage->writeConfig();
}
}
void KMFolder::setUnreadExpireAge( int age )
{
if( age >= 0 && age != mUnreadExpireAge ) {
mUnreadExpireAge = age;
mStorage->writeConfig();
}
}
void KMFolder::setUnreadExpireUnits( ExpireUnits units )
{
if (units >= expireNever && units < expireMaxUnits)
mUnreadExpireUnits = units;
}
void KMFolder::setReadExpireAge( int age )
{
if( age >= 0 && age != mReadExpireAge ) {
mReadExpireAge = age;
mStorage->writeConfig();
}
}
void KMFolder::setReadExpireUnits( ExpireUnits units )
{
if (units >= expireNever && units <= expireMaxUnits)
mReadExpireUnits = units;
}
void KMFolder::setExpireAction( ExpireAction a )
{
if ( a != mExpireAction ) {
mExpireAction = a;
mStorage->writeConfig();
}
}
void KMFolder::setExpireToFolderId( const QString& id )
{
if ( id != mExpireToFolderId ) {
mExpireToFolderId = id;
mStorage->writeConfig();
}
}
static int daysToExpire( int number, ExpireUnits units )
{
switch (units) {
case expireDays: // Days
return number;
case expireWeeks: // Weeks
return number * 7;
case expireMonths: // Months - this could be better rather than assuming 31day months.
return number * 31;
default: // this avoids a compiler warning (not handled enumeration values)
;
}
return -1;
}
void KMFolder::daysToExpire(int& unreadDays, int& readDays) {
unreadDays = ::daysToExpire( getUnreadExpireAge(), getUnreadExpireUnits() );
readDays = ::daysToExpire( getReadExpireAge(), getReadExpireUnits() );
}
void KMFolder::expireOldMessages( bool immediate )
{
KMail::ScheduledExpireTask* task = new KMail::ScheduledExpireTask(this, immediate);
kmkernel->jobScheduler()->registerTask( task );
if ( immediate ) {
// #82259: compact after expiring.
compact( CompactLater );
}
}
void KMFolder::compact( CompactOptions options )
{
if ( options == CompactLater ) {
KMail::ScheduledCompactionTask* task = new KMail::ScheduledCompactionTask(this, false);
kmkernel->jobScheduler()->registerTask( task );
} else {
mStorage->compact( options == CompactSilentlyNow );
}
}
KMFolder* KMFolder::trashFolder() const
{
return mStorage ? mStorage->trashFolder() : 0;
}
int KMFolder::writeIndex( bool createEmptyIndex )
{
return mStorage->writeIndex( createEmptyIndex );
}
void KMFolder::fillMsgDict( KMMsgDict* dict )
{
mStorage->fillMsgDict( dict );
}
int KMFolder::writeMsgDict( KMMsgDict* dict)
{
return mStorage->writeMsgDict( dict );
}
int KMFolder::touchMsgDict()
{
return mStorage->touchMsgDict();
}
int KMFolder::appendtoMsgDict( int idx )
{
return mStorage->appendtoMsgDict( idx );
}
void KMFolder::setRDict( KMMsgDictREntry* rentry )
{
mStorage->setRDict( rentry );
}
KMMsgDictREntry* KMFolder::rDict() const
{
return mStorage->rDict();
}
void KMFolder::setStatus( int idx, KMMsgStatus status, bool toggle )
{
mStorage->setStatus( idx, status, toggle );
}
void KMFolder::setStatus( QValueList<int>& ids, KMMsgStatus status,
bool toggle )
{
mStorage->setStatus( ids, status, toggle);
}
void KMFolder::setIconPaths( const QString &normalPath,
const QString &unreadPath )
{
mNormalIconPath = normalPath;
mUnreadIconPath = unreadPath;
mStorage->writeConfig();
emit iconsChanged();
}
void KMFolder::removeJobs()
{
mStorage->removeJobs();
}
size_t KMFolder::crlf2lf( char* str, const size_t strLen )
{
return FolderStorage::crlf2lf( str, strLen );
}
int KMFolder::updateIndex()
{
return mStorage->updateIndex();
}
void KMFolder::reallyAddMsg( KMMessage* aMsg )
{
mStorage->reallyAddMsg( aMsg );
}
void KMFolder::reallyAddCopyOfMsg( KMMessage* aMsg )
{
mStorage->reallyAddCopyOfMsg( aMsg );
}
#include "kmfolder.moc"