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.
 
 
 

601 lines
16 KiB

// kmfoldermgr.cpp
#include "kmfoldermgr.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <QDir>
#include <QList>
#include <kde_file.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kconfig.h>
#include <kdebug.h>
#include <krandom.h>
#include <kconfiggroup.h>
#include "kmmainwin.h"
#include "kmfiltermgr.h"
#include "folderstorage.h"
#include "kmfolder.h"
#include "kmfoldercachedimap.h"
#include "kmacctcachedimap.h"
#include "renamejob.h"
#include "copyfolderjob.h"
using KMail::RenameJob;
using KMail::CopyFolderJob;
//-----------------------------------------------------------------------------
KMFolderMgr::KMFolderMgr(const QString& aBasePath, KMFolderDirType dirType):
QObject(), mDir(this, QString(), dirType)
{
if ( dirType == KMStandardDir )
mDir.setBaseURL( I18N_NOOP("Local Folders") );
mQuiet = 0;
mChanged = false;
setBasePath(aBasePath);
mRemoveOrig = 0;
}
//-----------------------------------------------------------------------------
KMFolderMgr::~KMFolderMgr()
{
mBasePath.clear();
}
//-----------------------------------------------------------------------------
void KMFolderMgr::expireAll() {
KConfig *config = KMKernel::config();
KConfigGroup group(config, "General");
int ret = KMessageBox::Continue;
if ( group.readEntry( "warn-before-expire", true ) ) {
ret = KMessageBox::warningContinueCancel(KMainWindow::memberList().first(),
i18n("Are you sure you want to expire old messages?"),
i18n("Expire Old Messages?"), KGuiItem(i18n("Expire")));
}
if (ret == KMessageBox::Continue) {
expireAllFolders( true /*immediate*/ );
}
}
#define DO_FOR_ALL(function, folder_code) \
QList<KMFolderNode*>::const_iterator it; \
for ( it = dir->constBegin(); it != dir->constEnd(); ++it ) { \
KMFolderNode* node = *it; \
if (node->isDir()) continue; \
KMFolder *folder = static_cast<KMFolder*>(node); \
folder_code \
KMFolderDir *child = folder->child(); \
if (child) \
function \
}
int KMFolderMgr::folderCount(KMFolderDir *dir)
{
int count = 0;
if (dir == 0)
dir = &mDir;
DO_FOR_ALL(
{
count += folderCount( child );
},
{
count++;
}
)
return count;
}
//-----------------------------------------------------------------------------
void KMFolderMgr::compactAllFolders(bool immediate, KMFolderDir* dir)
{
if (dir == 0)
dir = &mDir;
DO_FOR_ALL(
{
compactAllFolders( immediate, child );
},
{
if ( folder->needsCompacting() )
folder->compact( immediate ? KMFolder::CompactNow : KMFolder::CompactLater );
}
)
}
//-----------------------------------------------------------------------------
void KMFolderMgr::setBasePath(const QString& aBasePath)
{
assert(!aBasePath.isEmpty());
if (aBasePath.startsWith('~'))
mBasePath = QDir::homePath() + '/' + aBasePath.mid(1);
else
mBasePath = aBasePath;
QFileInfo info( mBasePath );
// FIXME We should ask for an alternative dir, rather than bailing out,
// I guess - till
// FIXME We also should return boolean value here instead if exiting - jstaniek
if ( info.exists() ) {
if ( !info.isDir() ) {
KMessageBox::sorry(0, i18n("'%1' does not appear to be a folder.\n"
"Please move the file out of the way.",
mBasePath ) );
::exit(-1);
}
if ( !info.isReadable() || !info.isWritable() ) {
KMessageBox::sorry(0, i18n("The permissions of the folder '%1' are "
"incorrect;\n"
"please make sure that you can view and modify "
"the content of this folder.",
mBasePath ) );
::exit(-1);
}
} else {
// ~/Mail (or whatever the user specified) doesn't exist, create it
if ( KDE_mkdir( QFile::encodeName( mBasePath ), S_IRWXU ) == -1 ) {
KMessageBox::sorry( 0, i18n( "KMail could not create folder '%1';\n"
"please make sure that you can view and "
"modify the content of the folder '%2'.",
mBasePath, QDir::homePath() ) );
::exit(-1);
}
}
mDir.setPath(mBasePath);
mDir.reload();
contentsChanged();
}
//-----------------------------------------------------------------------------
KMFolder* KMFolderMgr::createFolder(const QString& fName, bool sysFldr,
KMFolderType aFolderType,
KMFolderDir *aFolderDir)
{
KMFolder* fld;
KMFolderDir *fldDir = aFolderDir;
if (!aFolderDir)
fldDir = &mDir;
// check if this is a dimap folder and the folder we want to create has been deleted
// since the last sync
if ( fldDir->owner() && fldDir->owner()->folderType() == KMFolderTypeCachedImap ) {
KMFolderCachedImap *storage = static_cast<KMFolderCachedImap*>( fldDir->owner()->storage() );
KMAcctCachedImap *account = storage->account();
// guess imap path
QString imapPath = storage->imapPath();
if ( !imapPath.endsWith( '/' ) )
imapPath += '/';
imapPath += fName;
if ( account->isDeletedFolder( imapPath ) || account->isDeletedFolder( imapPath + '/' )
|| account->isPreviouslyDeletedFolder( imapPath )
|| account->isPreviouslyDeletedFolder( imapPath + '/' ) ) {
KMessageBox::error( 0, i18n("A folder with the same name has been deleted since the last mail check. "
"You need to check mails first before creating another folder with the same name."),
i18n("Could Not Create Folder") );
return 0;
}
}
fld = fldDir->createFolder(fName, sysFldr, aFolderType);
if (fld) {
if ( fld->id() == 0 )
fld->setId( createId() );
contentsChanged();
emit folderAdded(fld);
if (kmkernel->filterMgr())
kmkernel->filterMgr()->folderCreated(fld);
}
return fld;
}
//-----------------------------------------------------------------------------
KMFolder* KMFolderMgr::find(const QString& folderName, bool foldersOnly) const
{
QList<KMFolderNode*>::const_iterator it;
for ( it = mDir.begin(); it != mDir.end(); ++it )
{
KMFolderNode* node = *it;
if (node->isDir() && foldersOnly) continue;
if (node->name()==folderName) return (KMFolder*)node;
}
return 0;
}
//-----------------------------------------------------------------------------
KMFolder* KMFolderMgr::findById(const uint id) const
{
return findIdString( QString(), id );
}
//-----------------------------------------------------------------------------
KMFolder* KMFolderMgr::findIdString( const QString& folderId,
const uint id,
const KMFolderDir *dir ) const
{
if (!dir)
dir = &mDir;
DO_FOR_ALL(
{
KMFolder *folder = findIdString( folderId, id, child );
if ( folder )
return folder;
},
{
if ( ( !folderId.isEmpty() && folder->idString() == folderId ) ||
( id != 0 && folder->id() == id ) )
return folder;
}
)
return 0;
}
void KMFolderMgr::getFolderURLS( QStringList& flist, const QString& prefix,
const KMFolderDir *adir ) const
{
const KMFolderDir* dir = adir ? adir : &mDir;
DO_FOR_ALL(
{
getFolderURLS( flist, prefix + '/' + folder->name(), child );
},
{
flist << prefix + '/' + folder->name();
}
)
}
KMFolder* KMFolderMgr::getFolderByURL( const QString& vpath,
const QString& prefix,
const KMFolderDir *adir ) const
{
const KMFolderDir* dir = adir ? adir : &mDir;
DO_FOR_ALL(
{
QString a = prefix + '/' + folder->name();
KMFolder * mfolder = getFolderByURL( vpath, a,child );
if ( mfolder )
return mfolder;
},
{
QString comp = prefix + '/' + folder->name();
if ( comp == vpath )
return folder;
}
)
return 0;
}
//-----------------------------------------------------------------------------
KMFolder* KMFolderMgr::findOrCreate(const QString& aFolderName, bool sysFldr,
const uint id)
{
KMFolder* folder = 0;
if ( id == 0 )
folder = find(aFolderName);
else
folder = findById(id);
if (!folder)
{
static bool know_type = false;
static KMFolderType type = KMFolderTypeMaildir;
if (know_type == false)
{
know_type = true;
KConfig *config = KMKernel::config();
KConfigGroup group(config, "General");
if (group.hasKey("default-mailbox-format"))
{
if ( group.readEntry("default-mailbox-format", 1 ) == 0 )
type = KMFolderTypeMbox;
}
}
folder = createFolder(aFolderName, sysFldr, type);
if (!folder) {
KMessageBox::error(0,(i18n("Cannot create file `%1' in %2.\nKMail cannot start without it.", aFolderName, mBasePath)));
exit(-1);
}
if ( id > 0 )
folder->setId( id );
}
return folder;
}
//-----------------------------------------------------------------------------
void KMFolderMgr::remove(KMFolder* aFolder)
{
if ( !aFolder )
return;
// remember the original folder to trigger contentsChanged later
if ( !mRemoveOrig )
mRemoveOrig = aFolder;
if ( aFolder->child() )
{
// call remove for every child
KMFolderNodeList children = *aFolder->child();
foreach( KMFolderNode *child, children ) {
if ( child->isDir() )
continue;
KMFolder *folder = static_cast<KMFolder*>( child );
remove( folder );
}
}
emit folderRemoved(aFolder);
removeFolder(aFolder);
}
void KMFolderMgr::removeFolder(KMFolder* aFolder)
{
connect(aFolder, SIGNAL(removed(KMFolder*, bool)),
this, SLOT(removeFolderAux(KMFolder*, bool)));
aFolder->remove();
}
KMFolder* KMFolderMgr::parentFolder( KMFolder* folder )
{
// find the parent folder by stripping "." and ".directory" from the name
KMFolderDir* fdir = folder->parent();
QString parentName = fdir->name();
parentName = parentName.mid( 1, parentName.length()-11 );
KMFolderNode* parent = fdir->hasNamedFolder( parentName );
if ( !parent && fdir->parent() ) // dimap obviously has a different structure
parent = fdir->parent()->hasNamedFolder( parentName );
KMFolder* parentF = 0;
if ( parent )
parentF = dynamic_cast<KMFolder*>( parent );
return parentF;
}
void KMFolderMgr::removeFolderAux(KMFolder* aFolder, bool success)
{
if (!success) {
mRemoveOrig = 0;
return;
}
KMFolderDir* fdir = aFolder->parent();
QList<KMFolderNode*>::const_iterator it;
for ( it = fdir->constBegin(); it != fdir->constEnd(); ++it ) {
KMFolderNode* fN = *it;
if (fN->isDir() && (fN->name() == '.' + aFolder->fileName() + ".directory")) {
removeDirAux(static_cast<KMFolderDir*>(fN));
break;
}
}
KMFolder* parentF = parentFolder( aFolder );
// aFolder will be deleted by the next call!
aFolder->parent()->removeAll(aFolder);
// update the children state
if ( parentF ) {
if ( parentF != aFolder ) {
parentF->storage()->updateChildrenState();
}
}
else
kWarning(5006) << "Can not find parent folder for " << aFolder->label().toUtf8().data();
if (aFolder == mRemoveOrig) {
// call only if we're removing the original parent folder
contentsChanged();
mRemoveOrig = 0;
}
}
void KMFolderMgr::removeDirAux(KMFolderDir* aFolderDir)
{
QDir dir;
QString folderDirLocation = aFolderDir->path();
aFolderDir->clear();
aFolderDir->parent()->removeAll(aFolderDir);
dir.rmdir(folderDirLocation);
}
//-----------------------------------------------------------------------------
KMFolderRootDir& KMFolderMgr::dir(void)
{
return mDir;
}
//-----------------------------------------------------------------------------
void KMFolderMgr::contentsChanged(void)
{
if (mQuiet) mChanged = true;
else emit changed();
}
//-----------------------------------------------------------------------------
void KMFolderMgr::reload(void)
{
}
//-----------------------------------------------------------------------------
void KMFolderMgr::createFolderList(QStringList *str,
QList<QPointer<KMFolder> > *folders)
{
createFolderList( str, folders, 0, "" );
}
//-----------------------------------------------------------------------------
void KMFolderMgr::createI18nFolderList(QStringList *str,
QList<QPointer<KMFolder> > *folders)
{
createFolderList( str, folders, 0, QString(), true );
}
//-----------------------------------------------------------------------------
void KMFolderMgr::createFolderList(QStringList *str,
QList<QPointer<KMFolder> > *folders,
KMFolderDir *adir,
const QString& prefix,
bool i18nized)
{
KMFolderDir* dir = adir ? adir : &mDir;
DO_FOR_ALL(
{
createFolderList(str, folders, child, " " + prefix, i18nized );
},
{
if (i18nized)
str->append(prefix + folder->label());
else
str->append(prefix + folder->name());
folders->append( folder );
}
)
}
//-----------------------------------------------------------------------------
void KMFolderMgr::syncAllFolders( KMFolderDir *adir )
{
KMFolderDir* dir = adir ? adir : &mDir;
DO_FOR_ALL(
{
syncAllFolders(child);
},
{
if (folder->isOpened())
folder->sync();
}
)
}
//-----------------------------------------------------------------------------
/**
* Check each folder in turn to see if it is configured to
* AutoExpire. If so, expire old messages.
*
* Should be called with 0 first time around.
*/
void KMFolderMgr::expireAllFolders(bool immediate, KMFolderDir *adir) {
KMFolderDir *dir = adir ? adir : &mDir;
DO_FOR_ALL(
{
expireAllFolders(immediate, child);
},
{
if (folder->isAutoExpire()) {
folder->expireOldMessages( immediate );
}
}
)
}
//-----------------------------------------------------------------------------
void KMFolderMgr::quiet(bool beQuiet)
{
if (beQuiet)
mQuiet++;
else {
mQuiet--;
if (mQuiet <= 0)
{
mQuiet = 0;
if (mChanged) emit changed();
mChanged = false;
}
}
}
//-----------------------------------------------------------------------------
void KMFolderMgr::tryReleasingFolder(KMFolder* f, KMFolderDir* adir)
{
KMFolderDir* dir = adir ? adir : &mDir;
DO_FOR_ALL(
{
tryReleasingFolder(f, child);
},
{
if (folder->isOpened())
folder->storage()->tryReleasingFolder(f);
}
)
}
//-----------------------------------------------------------------------------
uint KMFolderMgr::createId()
{
int newId;
do
{
newId = KRandom::random();
} while ( findById( newId ) != 0 );
return newId;
}
//-----------------------------------------------------------------------------
void KMFolderMgr::moveFolder( KMFolder* folder, KMFolderDir *newParent )
{
renameFolder( folder, folder->name(), newParent );
}
//-----------------------------------------------------------------------------
void KMFolderMgr::renameFolder( KMFolder* folder, const QString& newName,
KMFolderDir *newParent )
{
RenameJob* job = new RenameJob( folder->storage(), newName, newParent );
connect( job, SIGNAL( renameDone( const QString&, bool ) ),
this, SLOT( slotRenameDone( const QString&, bool ) ) );
connect( job, SIGNAL( renameDone( const QString&, bool ) ),
this, SIGNAL( folderMoveOrCopyOperationFinished() ) );
job->start();
}
//-----------------------------------------------------------------------------
void KMFolderMgr::copyFolder( KMFolder* folder, KMFolderDir *newParent )
{
kDebug(5006) <<"Copy folder:" << folder->prettyUrl();
CopyFolderJob* job = new CopyFolderJob( folder->storage(), newParent );
connect( job, SIGNAL( folderCopyComplete( bool ) ),
this, SIGNAL( folderMoveOrCopyOperationFinished() ) );
job->start();
}
//-----------------------------------------------------------------------------
void KMFolderMgr::slotRenameDone( const QString&, bool success )
{
kDebug(5006) << success;
}
#include "kmfoldermgr.moc"