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.
605 lines
16 KiB
605 lines
16 KiB
// kmfoldermgr.cpp
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
#include <qdir.h>
|
|
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kconfig.h>
|
|
#include <kdebug.h>
|
|
#include <kapplication.h>
|
|
|
|
#include "kmmainwin.h"
|
|
#include "kmfiltermgr.h"
|
|
#include "kmfoldermgr.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::null, dirType)
|
|
{
|
|
if ( dirType == KMStandardDir )
|
|
mDir.setBaseURL( I18N_NOOP("Local Folders") );
|
|
mQuiet = 0;
|
|
mChanged = FALSE;
|
|
setBasePath(aBasePath);
|
|
mRemoveOrig = 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
KMFolderMgr::~KMFolderMgr()
|
|
{
|
|
mBasePath = QString::null;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void KMFolderMgr::expireAll() {
|
|
KConfig *config = KMKernel::config();
|
|
KConfigGroupSaver saver(config, "General");
|
|
int ret = KMessageBox::Continue;
|
|
|
|
if (config->readBoolEntry("warn-before-expire", true)) {
|
|
ret = KMessageBox::warningContinueCancel(KMainWindow::memberList->first(),
|
|
i18n("Are you sure you want to expire old messages?"),
|
|
i18n("Expire Old Messages?"), i18n("Expire"));
|
|
}
|
|
|
|
if (ret == KMessageBox::Continue) {
|
|
expireAllFolders( true /*immediate*/ );
|
|
}
|
|
|
|
}
|
|
|
|
#define DO_FOR_ALL(function, folder_code) \
|
|
KMFolderNode* node; \
|
|
QPtrListIterator<KMFolderNode> it(*dir); \
|
|
for ( ; (node = it.current()); ) { \
|
|
++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.isNull());
|
|
|
|
if (aBasePath[0] == '~')
|
|
{
|
|
mBasePath = QDir::homeDirPath();
|
|
mBasePath.append("/");
|
|
mBasePath.append(aBasePath.mid(1));
|
|
}
|
|
else
|
|
mBasePath = aBasePath;
|
|
|
|
QFileInfo info( mBasePath );
|
|
|
|
// FIXME We should ask for an alternative dir, rather than bailing out,
|
|
// I guess - till
|
|
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.")
|
|
.arg( 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.")
|
|
.arg( mBasePath ) );
|
|
::exit(-1);
|
|
}
|
|
} else {
|
|
// ~/Mail (or whatever the user specified) doesn't exist, create it
|
|
if ( ::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'.")
|
|
.arg( mBasePath ).arg( QDir::homeDirPath() ) );
|
|
::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)
|
|
{
|
|
KMFolderNode* node;
|
|
|
|
for (node=mDir.first(); node; node=mDir.next())
|
|
{
|
|
if (node->isDir() && foldersOnly) continue;
|
|
if (node->name()==folderName) return (KMFolder*)node;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
KMFolder* KMFolderMgr::findById(const uint id)
|
|
{
|
|
return findIdString( QString::null, id );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
KMFolder* KMFolderMgr::findIdString( const QString& folderId,
|
|
const uint id,
|
|
KMFolderDir *dir )
|
|
{
|
|
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,
|
|
KMFolderDir *adir )
|
|
{
|
|
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,
|
|
KMFolderDir *adir )
|
|
{
|
|
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();
|
|
KConfigGroupSaver saver(config, "General");
|
|
if (config->hasKey("default-mailbox-format"))
|
|
{
|
|
if (config->readNumEntry("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.").arg(aFolderName).arg(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
|
|
KMFolderNode* node;
|
|
QPtrListIterator<KMFolderNode> it(*aFolder->child());
|
|
for ( ; (node = it.current()); )
|
|
{
|
|
++it;
|
|
if (node->isDir()) continue;
|
|
KMFolder *folder = static_cast<KMFolder*>(node);
|
|
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();
|
|
KMFolderNode* fN;
|
|
for (fN = fdir->first(); fN != 0; fN = fdir->next()) {
|
|
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()->remove(aFolder);
|
|
|
|
// update the children state
|
|
if ( parentF )
|
|
{
|
|
if ( parentF != aFolder )
|
|
{
|
|
parentF->storage()->updateChildrenState();
|
|
}
|
|
}
|
|
else
|
|
kdWarning(5006) << "Can not find parent folder" << endl;
|
|
|
|
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()->remove(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,
|
|
QValueList<QGuardedPtr<KMFolder> > *folders)
|
|
{
|
|
createFolderList( str, folders, 0, "" );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void KMFolderMgr::createI18nFolderList(QStringList *str,
|
|
QValueList<QGuardedPtr<KMFolder> > *folders)
|
|
{
|
|
createFolderList( str, folders, 0, QString::null, true );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void KMFolderMgr::createFolderList(QStringList *str,
|
|
QValueList<QGuardedPtr<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 = kapp->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( QString, bool ) ),
|
|
this, SLOT( slotRenameDone( QString, bool ) ) );
|
|
connect( job, SIGNAL( renameDone( QString, bool ) ),
|
|
this, SIGNAL( folderMoveOrCopyOperationFinished() ) );
|
|
job->start();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void KMFolderMgr::copyFolder( KMFolder* folder, KMFolderDir *newParent )
|
|
{
|
|
kdDebug(5006) << "Copy folder: " << folder->prettyURL() << endl;
|
|
CopyFolderJob* job = new CopyFolderJob( folder->storage(), newParent );
|
|
connect( job, SIGNAL( folderCopyComplete( bool ) ),
|
|
this, SIGNAL( folderMoveOrCopyOperationFinished() ) );
|
|
job->start();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void KMFolderMgr::slotRenameDone( QString, bool success )
|
|
{
|
|
kdDebug(5006) << k_funcinfo << success << endl;
|
|
}
|
|
|
|
#include "kmfoldermgr.moc"
|