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.
tdepim/kmail/kmfolderimap.cpp

2445 lines
81 KiB

/**
* kmfolderimap.cpp
*
* Copyright (c) 2001 Kurt Granroth <granroth@kde.org>
* Copyright (c) 2000-2002 Michael Haeckel <haeckel@kde.org>
*
* This file is based on kmacctimap.coo by Michael Haeckel which was
* based on popaccount.cpp by Don Sanders
*
* 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; version 2 of the License
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "kmfolder.h"
#include "kmfolderimap.h"
#include "kmfoldermbox.h"
#include "kmfoldertree.h"
#include "kmmsgdict.h"
#include "undostack.h"
#include "kmfoldermgr.h"
#include "kmfiltermgr.h"
#include "kmmsgdict.h"
#include "imapaccountbase.h"
using KMail::ImapAccountBase;
#include "imapjob.h"
using KMail::ImapJob;
#include "attachmentstrategy.h"
using KMail::AttachmentStrategy;
#include "progressmanager.h"
using KPIM::ProgressItem;
using KPIM::ProgressManager;
#include "listjob.h"
using KMail::ListJob;
#include "kmsearchpattern.h"
#include "searchjob.h"
using KMail::SearchJob;
#include "renamejob.h"
using KMail::RenameJob;
#include "acljobs.h"
#include <kdebug.h>
#include <kio/scheduler.h>
#include <kconfig.h>
#include <tqbuffer.h>
#include <tqtextcodec.h>
#include <tqstylesheet.h>
#include <assert.h>
KMFolderImap::KMFolderImap(KMFolder* folder, const char* aName)
: KMFolderMbox(folder, aName),
mUploadAllFlags( false )
{
mContentState = imapNoInformation;
mSubfolderState = imapNoInformation;
mAccount = 0;
mIsSelected = false;
mLastUid = 0;
mCheckFlags = true;
mCheckMail = true;
mCheckingValidity = false;
mUserRights = 0;
mUserRightsState = KMail::ACLJobs::NotFetchedYet;
mAlreadyRemoved = false;
mHasChildren = ChildrenUnknown;
mMailCheckProgressItem = 0;
mListDirProgressItem = 0;
mAddMessageProgressItem = 0;
mReadOnly = false;
connect (this, TQT_SIGNAL( folderComplete( KMFolderImap*, bool ) ),
this, TQT_SLOT( slotCompleteMailCheckProgress()) );
}
KMFolderImap::~KMFolderImap()
{
if (mAccount) {
mAccount->removeSlaveJobsForFolder( folder() );
/* Now that we've removed ourselves from the accounts jobs map, kill all
ongoing operations and reset mailcheck if we were deleted during an
ongoing mailcheck of our account. Not very gracefull, but safe, and the
only way I can see to reset the account state cleanly. */
if ( mAccount->checkingMail( folder() ) ) {
mAccount->killAllJobs();
}
}
writeConfig();
if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
mMetaDataMap.setAutoDelete( true );
mMetaDataMap.clear();
mUidMetaDataMap.setAutoDelete( true );
mUidMetaDataMap.clear();
}
//-----------------------------------------------------------------------------
void KMFolderImap::reallyDoClose(const char* owner)
{
// FIXME is this still needed?
if (account())
account()->ignoreJobsForFolder( folder() );
int idx = count();
while (--idx >= 0) {
if ( mMsgList[idx]->isMessage() ) {
KMMessage *msg = static_cast<KMMessage*>(mMsgList[idx]);
if (msg->transferInProgress())
msg->setTransferInProgress( false );
}
}
KMFolderMbox::reallyDoClose( owner );
}
KMFolder* KMFolderImap::trashFolder() const
{
TQString trashStr = account()->trash();
return kmkernel->imapFolderMgr()->findIdString( trashStr );
}
//-----------------------------------------------------------------------------
KMMessage* KMFolderImap::getMsg(int idx)
{
if(!(idx >= 0 && idx <= count()))
return 0;
KMMsgBase* mb = getMsgBase(idx);
if (!mb) return 0;
if (mb->isMessage())
{
return ((KMMessage*)mb);
} else {
KMMessage* msg = FolderStorage::getMsg( idx );
if ( msg ) // set it incomplete as the msg was not transferred from the server
msg->setComplete( false );
return msg;
}
}
//-----------------------------------------------------------------------------
KMAcctImap* KMFolderImap::account() const
{
if ( !mAccount ) {
KMFolderDir *parentFolderDir = dynamic_cast<KMFolderDir*>( folder()->tqparent() );
if ( !parentFolderDir ) {
kdWarning() << k_funcinfo << "No tqparent folder dir found for " << name() << endl;
return 0;
}
KMFolder *parentFolder = parentFolderDir->owner();
if ( !parentFolder ) {
kdWarning() << k_funcinfo << "No tqparent folder found for " << name() << endl;
return 0;
}
KMFolderImap *parentStorage = dynamic_cast<KMFolderImap*>( parentFolder->storage() );
if ( parentStorage )
mAccount = parentStorage->account();
}
return mAccount;
}
void KMFolderImap::setAccount(KMAcctImap *aAccount)
{
mAccount = aAccount;
if( !folder() || !folder()->child() ) return;
KMFolderNode* node;
for (node = folder()->child()->first(); node;
node = folder()->child()->next())
{
if (!node->isDir())
static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::readConfig()
{
KConfig* config = KMKernel::config();
KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
mCheckMail = config->readBoolEntry("checkmail", true);
mUidValidity = config->readEntry("UidValidity");
if ( mImapPath.isEmpty() ) {
setImapPath( config->readEntry("ImapPath") );
}
if (TQString(name()).upper() == "INBOX" && mImapPath == "/INBOX/")
{
folder()->setSystemFolder( true );
folder()->setLabel( i18n("inbox") );
}
mNoContent = config->readBoolEntry("NoContent", false);
mReadOnly = config->readBoolEntry("ReadOnly", false);
mUploadAllFlags = config->readBoolEntry( "UploadAllFlags", true );
mPermanentFlags = config->readNumEntry( "PermanentFlags", 31 /* default flags */ );
KMFolderMbox::readConfig();
}
//-----------------------------------------------------------------------------
void KMFolderImap::writeConfig()
{
KConfig* config = KMKernel::config();
KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
config->writeEntry("checkmail", mCheckMail);
config->writeEntry("UidValidity", mUidValidity);
config->writeEntry("ImapPath", mImapPath);
config->writeEntry("NoContent", mNoContent);
config->writeEntry("ReadOnly", mReadOnly);
config->writeEntry( "UploadAllFlags", mUploadAllFlags );
config->writeEntry( "PermanentFlags", mPermanentFlags );
KMFolderMbox::writeConfig();
}
//-----------------------------------------------------------------------------
void KMFolderImap::remove()
{
if ( mAlreadyRemoved || !account() )
{
// override
FolderStorage::remove();
return;
}
KURL url = account()->getUrl();
url.setPath(imapPath());
if ( account()->makeConnection() == ImapAccountBase::Error ||
imapPath().isEmpty() )
{
emit removed(folder(), false);
return;
}
KIO::SimpleJob *job = KIO::file_delete(url, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd(url.url());
jd.progressItem = ProgressManager::createProgressItem(
"ImapFolderRemove" + ProgressManager::getUniqueID(),
i18n("Removing folder"),
i18n( "URL: %1" ).tqarg( TQStyleSheet::escape( folder()->prettyURL() ) ),
false,
account()->useSSL() || account()->useTLS() );
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
this, TQT_SLOT(slotRemoveFolderResult(KIO::Job *)));
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotRemoveFolderResult(KIO::Job *job)
{
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
if (job->error())
{
account()->handleJobError( job, i18n("Error while removing a folder.") );
emit removed(folder(), false);
} else {
account()->removeJob(it);
FolderStorage::remove();
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::removeMsg(int idx, bool quiet)
{
if (idx < 0)
return;
if (!quiet)
{
KMMessage *msg = getMsg(idx);
deleteMessage(msg);
}
mLastUid = 0;
KMFolderMbox::removeMsg(idx);
}
void KMFolderImap::removeMsg( const TQPtrList<KMMessage>& msgList, bool quiet )
{
if ( msgList.isEmpty() ) return;
if (!quiet)
deleteMessage(msgList);
mLastUid = 0;
/* Remove the messages from the local store as well.
We don't call KMFolderInherited::removeMsg(TQPtrList<KMMessage>) but
iterate ourselves, as that would call KMFolderImap::removeMsg(int)
and not the one from the store we want to be used. */
TQPtrListIterator<KMMessage> it( msgList );
KMMessage *msg;
while ( (msg = it.current()) != 0 ) {
++it;
int idx = find(msg);
assert( idx != -1);
// ATTENTION port me to maildir
KMFolderMbox::removeMsg(idx, quiet);
}
}
//-----------------------------------------------------------------------------
int KMFolderImap::rename( const TQString& newName, KMFolderDir *aParent )
{
if ( !aParent )
KMFolderMbox::rename( newName );
kmkernel->folderMgr()->contentsChanged();
return 0;
}
//-----------------------------------------------------------------------------
void KMFolderImap::addMsgQuiet(KMMessage* aMsg)
{
KMFolder *aFolder = aMsg->tqparent();
TQ_UINT32 serNum = 0;
aMsg->setTransferInProgress( false );
if (aFolder) {
serNum = aMsg->getMsgSerNum();
kmkernel->undoStack()->pushSingleAction( serNum, aFolder, folder() );
int idx = aFolder->find( aMsg );
assert( idx != -1 );
aFolder->take( idx );
}
if ( !account()->hasCapability("uidplus") ) {
// Remember the status with the MD5 as key
// so it can be transfered to the new message
mMetaDataMap.insert( aMsg->msgIdMD5(),
new KMMsgMetaData(aMsg->status(), serNum) );
}
delete aMsg;
aMsg = 0;
getFolder();
}
//-----------------------------------------------------------------------------
void KMFolderImap::addMsgQuiet(TQPtrList<KMMessage> msgList)
{
if ( mAddMessageProgressItem )
{
mAddMessageProgressItem->setComplete();
mAddMessageProgressItem = 0;
}
KMFolder *aFolder = msgList.first()->tqparent();
int undoId = -1;
bool uidplus = account()->hasCapability("uidplus");
for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
{
if ( undoId == -1 )
undoId = kmkernel->undoStack()->newUndoAction( aFolder, folder() );
if ( msg->getMsgSerNum() > 0 )
kmkernel->undoStack()->addMsgToAction( undoId, msg->getMsgSerNum() );
if ( !uidplus ) {
// Remember the status with the MD5 as key
// so it can be transfered to the new message
mMetaDataMap.insert( msg->msgIdMD5(),
new KMMsgMetaData(msg->status(), msg->getMsgSerNum()) );
}
msg->setTransferInProgress( false );
}
if ( aFolder ) {
aFolder->take( msgList );
} else {
kdDebug(5006) << k_funcinfo << "no tqparent" << endl;
}
msgList.setAutoDelete(true);
msgList.clear();
getFolder();
}
//-----------------------------------------------------------------------------
int KMFolderImap::addMsg(KMMessage* aMsg, int* aIndex_ret)
{
TQPtrList<KMMessage> list;
list.append(aMsg);
TQValueList<int> index;
int ret = addMsg(list, index);
aIndex_ret = &index.first();
return ret;
}
int KMFolderImap::addMsg(TQPtrList<KMMessage>& msgList, TQValueList<int>& aIndex_ret)
{
KMMessage *aMsg = msgList.getFirst();
KMFolder *msgParent = aMsg->tqparent();
ImapJob *imapJob = 0;
if (msgParent)
{
if (msgParent->folderType() == KMFolderTypeImap)
{
if (static_cast<KMFolderImap*>(msgParent->storage())->account() == account())
{
// make sure the messages won't be deleted while we work with them
for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
msg->setTransferInProgress(true);
if (folder() == msgParent)
{
// transfer the whole message, e.g. a draft-message is canceled and re-added to the drafts-folder
for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
{
if (!msg->isComplete())
{
int idx = msgParent->find(msg);
assert(idx != -1);
msg = msgParent->getMsg(idx);
}
imapJob = new ImapJob(msg, ImapJob::tPutMessage, this);
connect(imapJob, TQT_SIGNAL(messageStored(KMMessage*)),
TQT_SLOT(addMsgQuiet(KMMessage*)));
connect(imapJob, TQT_SIGNAL(result(KMail::FolderJob*)),
TQT_SLOT(slotCopyMsgResult(KMail::FolderJob*)));
imapJob->start();
}
} else {
// get the messages and the uids
TQValueList<ulong> uids;
getUids(msgList, uids);
// get the sets (do not sort the uids)
TQStringList sets = makeSets(uids, false);
for ( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
{
// we need the messages that belong to the current set to pass them to the ImapJob
TQPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
if ( temp_msgs.isEmpty() ) kdDebug(5006) << "Wow! KMFolderImap::splitMessageList() returned an empty list!" << endl;
imapJob = new ImapJob(temp_msgs, *it, ImapJob::tMoveMessage, this);
connect(imapJob, TQT_SIGNAL(messageCopied(TQPtrList<KMMessage>)),
TQT_SLOT(addMsgQuiet(TQPtrList<KMMessage>)));
connect(imapJob, TQT_SIGNAL(result(KMail::FolderJob*)),
TQT_SLOT(slotCopyMsgResult(KMail::FolderJob*)));
imapJob->start();
}
}
return 0;
}
else
{
// different account, check if messages can be added
TQPtrListIterator<KMMessage> it( msgList );
KMMessage *msg;
while ( (msg = it.current()) != 0 )
{
++it;
int index;
if (!canAddMsgNow(msg, &index)) {
aIndex_ret << index;
msgList.remove(msg);
} else {
if (!msg->transferInProgress())
msg->setTransferInProgress(true);
}
}
}
} // if imap
}
if ( !msgList.isEmpty() )
{
// transfer from local folders or other accounts
TQPtrListIterator<KMMessage> it( msgList );
KMMessage* msg;
while ( ( msg = it.current() ) != 0 )
{
++it;
if ( !msg->transferInProgress() )
msg->setTransferInProgress( true );
}
imapJob = new ImapJob( msgList, TQString(), ImapJob::tPutMessage, this );
if ( !mAddMessageProgressItem && msgList.count() > 1 )
{
// use a tqparent progress if we have more than 1 message
// otherwise the normal progress is more accurate
mAddMessageProgressItem = ProgressManager::createProgressItem(
"Uploading"+ProgressManager::getUniqueID(),
i18n("Uploading message data"),
i18n("Destination folder: %1").tqarg( TQStyleSheet::escape( folder()->prettyURL() ) ),
true,
account()->useSSL() || account()->useTLS() );
mAddMessageProgressItem->setTotalItems( msgList.count() );
connect ( mAddMessageProgressItem, TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem*)),
account(), TQT_SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
imapJob->setParentProgressItem( mAddMessageProgressItem );
}
connect( imapJob, TQT_SIGNAL( messageCopied(TQPtrList<KMMessage>) ),
TQT_SLOT( addMsgQuiet(TQPtrList<KMMessage>) ) );
connect( imapJob, TQT_SIGNAL(result(KMail::FolderJob*)),
TQT_SLOT(slotCopyMsgResult(KMail::FolderJob*)) );
imapJob->start();
}
return 0;
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotCopyMsgResult( KMail::FolderJob* job )
{
kdDebug(5006) << k_funcinfo << job->error() << endl;
if ( job->error() ) // getFolder() will not be called in this case
emit folderComplete( this, false );
}
//-----------------------------------------------------------------------------
void KMFolderImap::copyMsg(TQPtrList<KMMessage>& msgList)
{
if ( !account()->hasCapability("uidplus") ) {
for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
// Remember the status with the MD5 as key
// so it can be transfered to the new message
mMetaDataMap.insert( msg->msgIdMD5(), new KMMsgMetaData(msg->status()) );
}
}
TQValueList<ulong> uids;
getUids(msgList, uids);
TQStringList sets = makeSets(uids, false);
for ( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
{
// we need the messages that belong to the current set to pass them to the ImapJob
TQPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
ImapJob *job = new ImapJob(temp_msgs, *it, ImapJob::tCopyMessage, this);
connect(job, TQT_SIGNAL(result(KMail::FolderJob*)),
TQT_SLOT(slotCopyMsgResult(KMail::FolderJob*)));
job->start();
}
}
//-----------------------------------------------------------------------------
TQPtrList<KMMessage> KMFolderImap::splitMessageList(const TQString& set,
TQPtrList<KMMessage>& msgList)
{
int lastcomma = set.findRev(",");
int lastdub = set.findRev(":");
int last = 0;
if (lastdub > lastcomma) last = lastdub;
else last = lastcomma;
last++;
if (last < 0) last = set.length();
// the last uid of the current set
const TQString last_uid = set.right(set.length() - last);
TQPtrList<KMMessage> temp_msgs;
TQString uid;
if (!last_uid.isEmpty())
{
TQPtrListIterator<KMMessage> it( msgList );
KMMessage* msg = 0;
while ( (msg = it.current()) != 0 )
{
// append the msg to the new list and delete it from the old
temp_msgs.append(msg);
uid.setNum( msg->UID() );
// remove modifies the current
msgList.remove(msg);
if (uid == last_uid) break;
}
}
else
{
// probably only one element
temp_msgs = msgList;
}
return temp_msgs;
}
//-----------------------------------------------------------------------------
KMMessage* KMFolderImap::take(int idx)
{
KMMsgBase* mb(mMsgList[idx]);
if (!mb) return 0;
if (!mb->isMessage()) readMsg(idx);
KMMessage *msg = static_cast<KMMessage*>(mb);
deleteMessage(msg);
mLastUid = 0;
return KMFolderMbox::take(idx);
}
void KMFolderImap::take(TQPtrList<KMMessage> msgList)
{
deleteMessage(msgList);
mLastUid = 0;
KMFolderMbox::take(msgList);
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotListNamespaces()
{
disconnect( account(), TQT_SIGNAL( connectionResult(int, const TQString&) ),
this, TQT_SLOT( slotListNamespaces() ) );
if ( account()->makeConnection() == ImapAccountBase::Error )
{
kdWarning(5006) << "slotListNamespaces - got no connection" << endl;
return;
} else if ( account()->makeConnection() == ImapAccountBase::Connecting )
{
// wait for the connectionResult
kdDebug(5006) << "slotListNamespaces - waiting for connection" << endl;
connect( account(), TQT_SIGNAL( connectionResult(int, const TQString&) ),
this, TQT_SLOT( slotListNamespaces() ) );
return;
}
kdDebug(5006) << "slotListNamespaces" << endl;
// reset subfolder states recursively
setSubfolderState( imapNoInformation );
mSubfolderState = imapListingInProgress;
account()->setHasInbox( false );
ImapAccountBase::ListType type = ImapAccountBase::List;
if ( account()->onlySubscribedFolders() )
type = ImapAccountBase::ListSubscribed;
ImapAccountBase::nsMap map = account()->namespaces();
TQStringList personal = map[ImapAccountBase::PersonalNS];
// start personal namespace listing and send it directly to slotListResult
for ( TQStringList::Iterator it = personal.begin(); it != personal.end(); ++it )
{
KMail::ListJob* job = new KMail::ListJob( account(), type, this,
account()->addPathToNamespace( *it ) );
job->setNamespace( *it );
job->setHonorLocalSubscription( true );
connect( job, TQT_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
this, TQT_SLOT(slotListResult(const TQStringList&, const TQStringList&,
const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
job->start();
}
// and now we list all other namespaces and check them ourself
TQStringList ns = map[ImapAccountBase::OtherUsersNS];
ns += map[ImapAccountBase::SharedNS];
for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
{
KMail::ListJob* job = new KMail::ListJob( account(), type, this, account()->addPathToNamespace( *it ) );
job->setHonorLocalSubscription( true );
connect( job, TQT_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
this, TQT_SLOT(slotCheckNamespace(const TQStringList&, const TQStringList&,
const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
job->start();
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotCheckNamespace( const TQStringList& subfolderNames,
const TQStringList& subfolderPaths,
const TQStringList& subfolderMimeTypes,
const TQStringList& subfolderAttributes,
const ImapAccountBase::jobData& jobData )
{
kdDebug(5006) << "slotCheckNamespace - " << subfolderNames.join(",") << endl;
// get a correct foldername:
// strip / and make sure it does not contain the delimiter
TQString name = jobData.path.mid( 1, jobData.path.length()-2 );
name.remove( account()->delimiterForNamespace( name ) );
if ( name.isEmpty() ) {
// happens when an empty namespace is defined
slotListResult( subfolderNames, subfolderPaths,
subfolderMimeTypes, subfolderAttributes, jobData );
return;
}
folder()->createChildFolder();
KMFolderNode *node = 0;
for ( node = folder()->child()->first(); node;
node = folder()->child()->next())
{
if ( !node->isDir() && node->name() == name )
break;
}
if ( subfolderNames.isEmpty() )
{
if ( node )
{
kdDebug(5006) << "delete namespace folder " << name << endl;
KMFolder *fld = static_cast<KMFolder*>(node);
KMFolderImap* nsFolder = static_cast<KMFolderImap*>(fld->storage());
nsFolder->setAlreadyRemoved( true );
kmkernel->imapFolderMgr()->remove( fld );
}
} else {
if ( node )
{
// folder exists so pass on the attributes
kdDebug(5006) << "found namespace folder " << name << endl;
if ( !account()->listOnlyOpenFolders() )
{
KMFolderImap* nsFolder =
static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
nsFolder->slotListResult( subfolderNames, subfolderPaths,
subfolderMimeTypes, subfolderAttributes, jobData );
}
} else
{
// create folder
kdDebug(5006) << "create namespace folder " << name << endl;
KMFolder *fld = folder()->child()->createFolder( name );
if ( fld ) {
KMFolderImap* f = static_cast<KMFolderImap*> ( fld->storage() );
f->initializeFrom( this, account()->addPathToNamespace( name ),
"inode/directory" );
f->close( "kmfolderimap_create" );
if ( !account()->listOnlyOpenFolders() )
{
f->slotListResult( subfolderNames, subfolderPaths,
subfolderMimeTypes, subfolderAttributes, jobData );
}
}
kmkernel->imapFolderMgr()->contentsChanged();
}
}
}
//-----------------------------------------------------------------------------
bool KMFolderImap::listDirectory()
{
if ( !account() ||
( account() && account()->makeConnection() == ImapAccountBase::Error ) )
{
kdDebug(5006) << "KMFolderImap::listDirectory - got no connection" << endl;
return false;
}
if ( this == account()->rootFolder() )
{
// a new listing started
slotListNamespaces();
return true;
}
mSubfolderState = imapListingInProgress;
// get the folders
ImapAccountBase::ListType type = ImapAccountBase::List;
if ( account()->onlySubscribedFolders() )
type = ImapAccountBase::ListSubscribed;
KMail::ListJob* job = new KMail::ListJob( account(), type, this );
job->setParentProgressItem( account()->listDirProgressItem() );
job->setHonorLocalSubscription( true );
connect( job, TQT_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
this, TQT_SLOT(slotListResult(const TQStringList&, const TQStringList&,
const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
job->start();
return true;
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotListResult( const TQStringList& subfolderNames,
const TQStringList& subfolderPaths,
const TQStringList& subfolderMimeTypes,
const TQStringList& subfolderAttributes,
const ImapAccountBase::jobData& jobData )
{
mSubfolderState = imapFinished;
//kdDebug(5006) << label() << ": folderNames=" << subfolderNames << " folderPaths="
//<< subfolderPaths << " mimeTypes=" << subfolderMimeTypes << endl;
// don't react on changes
kmkernel->imapFolderMgr()->quiet(true);
bool root = ( this == account()->rootFolder() );
folder()->createChildFolder();
if ( root && !account()->hasInbox() )
{
// create the INBOX
initInbox();
}
// see if we have a better tqparent
// if you have a prefix that contains a folder (e.g "INBOX.") the folders
// need to be created underneath it
if ( root && !subfolderNames.empty() )
{
KMFolderImap* tqparent = findParent( subfolderPaths.first(), subfolderNames.first() );
if ( tqparent )
{
kdDebug(5006) << "KMFolderImap::slotListResult - pass listing to "
<< tqparent->label() << endl;
tqparent->slotListResult( subfolderNames, subfolderPaths,
subfolderMimeTypes, subfolderAttributes, jobData );
// cleanup
TQStringList list;
checkFolders( list, jobData.curNamespace );
// finish
emit directoryListingFinished( this );
kmkernel->imapFolderMgr()->quiet( false );
return;
}
}
bool emptyList = ( root && subfolderNames.empty() );
if ( !emptyList )
{
checkFolders( subfolderNames, jobData.curNamespace );
}
KMFolderImap *f = 0;
KMFolderNode *node = 0;
for ( uint i = 0; i < subfolderNames.count(); i++ )
{
bool settingsChanged = false;
// create folders if necessary
for ( node = folder()->child()->first(); node;
node = folder()->child()->next() ) {
if ( !node->isDir() && node->name() == subfolderNames[i] )
break;
}
if ( node ) {
f = static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
}
else if ( subfolderPaths[i].upper() != "/INBOX/" )
{
kdDebug(5006) << "create folder " << subfolderNames[i] << endl;
KMFolder *fld = folder()->child()->createFolder(subfolderNames[i]);
if ( fld ) {
f = static_cast<KMFolderImap*> ( fld->storage() );
f->close( "kmfolderimap_create" );
settingsChanged = true;
} else {
kdWarning(5006) << "can't create folder " << subfolderNames[i] << endl;
}
}
if ( f )
{
// sanity check
if ( f->imapPath().isEmpty() ) {
settingsChanged = true;
}
// update progress
account()->listDirProgressItem()->incCompletedItems();
account()->listDirProgressItem()->updateProgress();
account()->listDirProgressItem()->setqStatus( folder()->prettyURL() + i18n(" completed") );
f->initializeFrom( this, subfolderPaths[i], subfolderMimeTypes[i] );
f->setChildrenState( subfolderAttributes[i] );
if ( account()->listOnlyOpenFolders() &&
f->hasChildren() != FolderStorage::ChildrenUnknown )
{
settingsChanged = true;
}
if ( settingsChanged )
{
// tell the tree our information changed
kmkernel->imapFolderMgr()->contentsChanged();
}
if ( ( subfolderMimeTypes[i] == "message/directory" ||
subfolderMimeTypes[i] == "inode/directory" ) &&
!account()->listOnlyOpenFolders() )
{
f->listDirectory();
}
} else {
kdWarning(5006) << "can't find folder " << subfolderNames[i] << endl;
}
} // for subfolders
// now others should react on the changes
kmkernel->imapFolderMgr()->quiet( false );
emit directoryListingFinished( this );
account()->listDirProgressItem()->setComplete();
}
//-----------------------------------------------------------------------------
void KMFolderImap::initInbox()
{
KMFolderImap *f = 0;
KMFolderNode *node = 0;
for (node = folder()->child()->first(); node;
node = folder()->child()->next()) {
if (!node->isDir() && node->name() == "INBOX") break;
}
if (node) {
f = static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
} else {
f = static_cast<KMFolderImap*>
(folder()->child()->createFolder("INBOX", true)->storage());
if ( f )
{
f->folder()->setLabel( i18n("inbox") );
f->close( "kmfolderimap" );
}
kmkernel->imapFolderMgr()->contentsChanged();
}
if ( f ) {
f->initializeFrom( this, "/INBOX/", "message/directory" );
f->setChildrenState( TQString() );
}
// so we have an INBOX
account()->setHasInbox( true );
}
//-----------------------------------------------------------------------------
KMFolderImap* KMFolderImap::findParent( const TQString& path, const TQString& name )
{
TQString tqparent = path.left( path.length() - name.length() - 2 );
if ( tqparent.length() > 1 )
{
// extract name of the tqparent
tqparent = tqparent.right( tqparent.length() - 1 );
if ( tqparent != label() )
{
KMFolderNode *node = folder()->child()->first();
// look for a better tqparent
while ( node )
{
if ( node->name() == tqparent )
{
KMFolder* fld = static_cast<KMFolder*>(node);
KMFolderImap* imapFld = static_cast<KMFolderImap*>( fld->storage() );
return imapFld;
}
node = folder()->child()->next();
}
}
}
return 0;
}
//-----------------------------------------------------------------------------
void KMFolderImap::checkFolders( const TQStringList& subfolderNames,
const TQString& myNamespace )
{
TQPtrList<KMFolder> toRemove;
KMFolderNode *node = folder()->child()->first();
while ( node )
{
if ( !node->isDir() && subfolderNames.findIndex(node->name()) == -1 )
{
KMFolder* fld = static_cast<KMFolder*>(node);
KMFolderImap* imapFld = static_cast<KMFolderImap*>( fld->storage() );
// as more than one namespace can be listed in the root folder we need to make sure
// that the folder is within the current namespace
bool isInNamespace = ( myNamespace.isEmpty() ||
myNamespace == account()->namespaceForFolder( imapFld ) );
kdDebug(5006) << node->name() << " in namespace " << myNamespace << ":" <<
isInNamespace << endl;
// ignore some cases
TQString name = node->name();
bool ignore = ( ( this == account()->rootFolder() ) &&
( imapFld->imapPath() == "/INBOX/" ||
account()->isNamespaceFolder( name ) ||
!isInNamespace ) );
// additional sanity check for broken folders
if ( imapFld->imapPath().isEmpty() ) {
ignore = false;
}
if ( !ignore )
{
// remove the folder without server round trip
kdDebug(5006) << "checkFolders - " << node->name() << " disappeared" << endl;
imapFld->setAlreadyRemoved( true );
toRemove.append( fld );
} else {
kdDebug(5006) << "checkFolders - " << node->name() << " ignored" << endl;
}
}
node = folder()->child()->next();
}
// remove folders
for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() )
kmkernel->imapFolderMgr()->remove( doomed );
}
//-----------------------------------------------------------------------------
void KMFolderImap::initializeFrom( KMFolderImap* tqparent, TQString folderPath,
TQString mimeType )
{
setAccount( tqparent->account() );
setImapPath( folderPath );
setNoContent( mimeType == "inode/directory" );
setNoChildren( mimeType == "message/digest" );
}
//-----------------------------------------------------------------------------
bool KMFolderImap::mailCheckInProgress() const
{
return getContentState() != imapNoInformation &&
getContentState() != imapFinished;
}
//-----------------------------------------------------------------------------
void KMFolderImap::setChildrenState( TQString attributes )
{
// update tqchildren state
if ( attributes.find( "hastqchildren", 0, false ) != -1 )
{
setHasChildren( FolderStorage::HasChildren );
} else if ( attributes.find( "hasnotqchildren", 0, false ) != -1 ||
attributes.find( "noinferiors", 0, false ) != -1 )
{
setHasChildren( FolderStorage::HasNoChildren );
} else
{
if ( account()->listOnlyOpenFolders() ) {
setHasChildren( FolderStorage::HasChildren );
} else {
setHasChildren( FolderStorage::ChildrenUnknown );
}
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::checkValidity()
{
if (!account()) {
emit folderComplete(this, false);
close("checkvalidity");
return;
}
KURL url = account()->getUrl();
url.setPath(imapPath() + ";UID=0:0");
kdDebug(5006) << "KMFolderImap::checkValidity of: " << imapPath() << endl;
// Start with a clean slate
disconnect( account(), TQT_SIGNAL( connectionResult(int, const TQString&) ),
this, TQT_SLOT( checkValidity() ) );
KMAcctImap::ConnectionState connectionState = account()->makeConnection();
if ( connectionState == ImapAccountBase::Error ) {
kdDebug(5006) << "KMFolderImap::checkValidity - got no connection" << endl;
emit folderComplete(this, false);
mContentState = imapNoInformation;
close("checkvalidity");
return;
} else if ( connectionState == ImapAccountBase::Connecting ) {
// We'll wait for the connectionResult signal from the account. If it
// errors, the above will catch it.
kdDebug(5006) << "CheckValidity - waiting for connection" << endl;
connect( account(), TQT_SIGNAL( connectionResult(int, const TQString&) ),
this, TQT_SLOT( checkValidity() ) );
return;
}
// Only check once at a time.
if (mCheckingValidity) {
kdDebug(5006) << "KMFolderImap::checkValidity - already checking" << endl;
close("checkvalidity");
return;
}
// otherwise we already are inside a mailcheck
if ( !mMailCheckProgressItem ) {
ProgressItem* tqparent = ( account()->checkingSingleFolder() ? 0 :
account()->mailCheckProgressItem() );
mMailCheckProgressItem = ProgressManager::createProgressItem(
tqparent,
"MailCheck" + folder()->prettyURL(),
TQStyleSheet::escape( folder()->prettyURL() ),
i18n("checking"),
false,
account()->useSSL() || account()->useTLS() );
} else {
mMailCheckProgressItem->setProgress(0);
}
if ( account()->mailCheckProgressItem() ) {
account()->mailCheckProgressItem()->setqStatus( folder()->prettyURL() );
}
ImapAccountBase::jobData jd( url.url() );
KIO::SimpleJob *job = KIO::get(url, false, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
TQT_SLOT(slotCheckValidityResult(KIO::Job *)));
connect(job, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
TQT_SLOT(slotSimpleData(KIO::Job *, const TQByteArray &)));
// Only check once at a time.
mCheckingValidity = true;
}
//-----------------------------------------------------------------------------
ulong KMFolderImap::lastUid()
{
if ( mLastUid > 0 )
return mLastUid;
open("lastuid");
if (count() > 0)
{
KMMsgBase * base = getMsgBase(count()-1);
mLastUid = base->UID();
}
close("lastuid");
return mLastUid;
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotCheckValidityResult(KIO::Job * job)
{
kdDebug(5006) << "KMFolderImap::slotCheckValidityResult of: " << fileName() << endl;
mCheckingValidity = false;
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
if (job->error()) {
if ( job->error() != KIO::ERR_ACCESS_DENIED ) {
// we suppress access denied messages because they are normally a result of
// explicitely set ACLs. Do not save this information (e.g. setNoContent) so that
// we notice when this changes
account()->handleJobError( job, i18n("Error while querying the server status.") );
}
mContentState = imapNoInformation;
emit folderComplete(this, false);
close("checkvalidity");
} else {
TQCString cstr((*it).data.data(), (*it).data.size() + 1);
int a = cstr.find("X-uidValidity: ");
int b = cstr.find("\r\n", a);
TQString uidv;
if ( (b - a - 15) >= 0 )
uidv = cstr.mid(a + 15, b - a - 15);
a = cstr.find("X-Access: ");
b = cstr.find("\r\n", a);
TQString access;
if ( (b - a - 10) >= 0 )
access = cstr.mid(a + 10, b - a - 10);
mReadOnly = access == "Read only";
a = cstr.find("X-Count: ");
b = cstr.find("\r\n", a);
int exists = -1;
bool ok = false;
if ( (b - a - 9) >= 0 )
exists = cstr.mid(a + 9, b - a - 9).toInt(&ok);
if ( !ok ) exists = -1;
a = cstr.find( "X-PermanentFlags: " );
b = cstr.find( "\r\n", a );
if ( a >= 0 && (b - a - 18) >= 0 )
mPermanentFlags = cstr.mid( a + 18, b - a - 18 ).toInt(&ok);
if ( !ok ) mPermanentFlags = 0;
TQString startUid;
if (uidValidity() != uidv)
{
// uidValidity changed
kdDebug(5006) << k_funcinfo << "uidValidty changed from "
<< uidValidity() << " to " << uidv << endl;
if ( !uidValidity().isEmpty() )
{
account()->ignoreJobsForFolder( folder() );
mUidMetaDataMap.clear();
}
mLastUid = 0;
setUidValidity(uidv);
writeConfig();
} else {
if (!mCheckFlags)
startUid = TQString::number(lastUid() + 1);
}
account()->removeJob(it);
if ( mMailCheckProgressItem )
{
if ( startUid.isEmpty() ) {
// flags for all messages are loaded
mMailCheckProgressItem->setTotalItems( exists );
} else {
// only an approximation but doesn't hurt
int remain = exists - count();
if ( remain < 0 ) remain = 1;
mMailCheckProgressItem->setTotalItems( remain );
}
mMailCheckProgressItem->setCompletedItems( 0 );
}
reallyGetFolder(startUid);
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::getAndCheckFolder(bool force)
{
if (mNoContent)
return getFolder(force);
if ( account() )
account()->processNewMailInFolder( folder() );
if (force) {
// force an update
mCheckFlags = true;
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::getFolder(bool force)
{
mGuessedUnreadMsgs = -1;
if (mNoContent)
{
mContentState = imapFinished;
emit folderComplete(this, true);
return;
}
open("getfolder");
mContentState = imapListingInProgress;
if (force) {
// force an update
mCheckFlags = true;
}
checkValidity();
}
//-----------------------------------------------------------------------------
void KMFolderImap::reallyGetFolder(const TQString &startUid)
{
KURL url = account()->getUrl();
if ( account()->makeConnection() != ImapAccountBase::Connected )
{
mContentState = imapNoInformation;
emit folderComplete(this, false);
close("listfolder");
return;
}
quiet(true);
if (startUid.isEmpty())
{
if ( mMailCheckProgressItem )
mMailCheckProgressItem->setqStatus( i18n("Retrieving message status") );
url.setPath(imapPath() + ";SECTION=UID FLAGS");
KIO::SimpleJob *job = KIO::listDir(url, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd( url.url(), folder() );
jd.cancellable = true;
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
this, TQT_SLOT(slotListFolderResult(KIO::Job *)));
connect(job, TQT_SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
this, TQT_SLOT(slotListFolderEntries(KIO::Job *,
const KIO::UDSEntryList &)));
} else {
mContentState = imapDownloadInProgress;
if ( mMailCheckProgressItem )
mMailCheckProgressItem->setqStatus( i18n("Retrieving messages") );
url.setPath(imapPath() + ";UID=" + startUid
+ ":*;SECTION=ENVELOPE");
KIO::SimpleJob *newJob = KIO::get(url, false, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), newJob);
ImapAccountBase::jobData jd( url.url(), folder() );
jd.cancellable = true;
account()->insertJob(newJob, jd);
connect(newJob, TQT_SIGNAL(result(KIO::Job *)),
this, TQT_SLOT(slotGetLastMessagesResult(KIO::Job *)));
connect(newJob, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
this, TQT_SLOT(slotGetMessagesData(KIO::Job *, const TQByteArray &)));
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotListFolderResult(KIO::Job * job)
{
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
TQString uids;
if (job->error())
{
account()->handleJobError( job,
i18n("Error while listing the contents of the folder %1.").tqarg( label() ) );
account()->removeJob(it);
finishMailCheck( "listfolder", imapNoInformation );
return;
}
mCheckFlags = false;
TQStringList::Iterator uid;
/*
The code below does the following:
- for each mail in the local store and each entry we got from the server,
compare the local uid with the one from the server and update the status
flags of the mails
- for all mails that are not already locally present, start a job which
gets the envelope of each
- remove all locally present mails if the server does not list them anymore
*/
if ( count() ) {
int idx = 0, c, serverFlags;
ulong mailUid, serverUid;
uid = (*it).items.begin();
while ( idx < count() && uid != (*it).items.end() ) {
KMMsgBase *msgBase = getMsgBase( idx );
mailUid = msgBase->UID();
// parse the uid from the server and the flags out of the list from
// the server. Format: 1234, 1
c = (*uid).find(",");
serverUid = (*uid).left( c ).toLong();
serverFlags = (*uid).mid( c+1 ).toInt();
if ( mailUid < serverUid ) {
removeMsg( idx, true );
} else if ( mailUid == serverUid ) {
// if this is a read only folder, ignore status updates from the server
// since we can't write our status back our local version is what has to
// be considered correct.
if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) {
int supportedFlags = mUploadAllFlags ? 31 : mPermanentFlags;
if ( mReadOnly )
supportedFlags = INT_MAX;
flagsTotqStatus( msgBase, serverFlags, false, supportedFlags );
} else
seenFlagTotqStatus( msgBase, serverFlags, false );
idx++;
uid = (*it).items.remove(uid);
if ( msgBase->getMsgSerNum() > 0 ) {
saveMsgMetaData( static_cast<KMMessage*>(msgBase) );
}
}
else break; // happens only, if deleted mails reappear on the server
}
// remove all remaining entries in the local cache, they are no longer
// present on the server
while (idx < count()) removeMsg(idx, true);
}
// strip the flags from the list of uids, so it can be reused
for (uid = (*it).items.begin(); uid != (*it).items.end(); ++uid)
(*uid).truncate((*uid).find(","));
ImapAccountBase::jobData jd( TQString(), (*it).tqparent );
jd.total = (*it).items.count();
if (jd.total == 0)
{
finishMailCheck( "listfolder", imapFinished );
account()->removeJob(it);
return;
}
if ( mMailCheckProgressItem )
{
// next step for the progressitem
mMailCheckProgressItem->setCompletedItems( 0 );
mMailCheckProgressItem->setTotalItems( jd.total );
mMailCheckProgressItem->setProgress( 0 );
mMailCheckProgressItem->setqStatus( i18n("Retrieving messages") );
}
TQStringList sets;
uid = (*it).items.begin();
if (jd.total == 1) sets.append(*uid + ":" + *uid);
else sets = makeSets( (*it).items );
account()->removeJob(it); // don't use *it below
// Now kick off the getting of envelopes for the new mails in the folder
for (TQStringList::Iterator i = sets.begin(); i != sets.end(); ++i)
{
mContentState = imapDownloadInProgress;
KURL url = account()->getUrl();
url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE");
KIO::SimpleJob *newJob = KIO::get(url, false, false);
jd.url = url.url();
KIO::Scheduler::assignJobToSlave(account()->slave(), newJob);
account()->insertJob(newJob, jd);
connect(newJob, TQT_SIGNAL(result(KIO::Job *)),
this, (i == sets.at(sets.count() - 1))
? TQT_SLOT(slotGetLastMessagesResult(KIO::Job *))
: TQT_SLOT(slotGetMessagesResult(KIO::Job *)));
connect(newJob, TQT_SIGNAL(data(KIO::Job *, const TQByteArray &)),
this, TQT_SLOT(slotGetMessagesData(KIO::Job *, const TQByteArray &)));
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotListFolderEntries(KIO::Job * job,
const KIO::UDSEntryList & uds)
{
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
TQString mimeType, name;
long int flags = 0;
for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
udsIt != uds.end(); udsIt++)
{
for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
eIt != (*udsIt).end(); eIt++)
{
if ((*eIt).m_uds == KIO::UDS_NAME)
name = (*eIt).m_str;
else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
mimeType = (*eIt).m_str;
else if ((*eIt).m_uds == KIO::UDS_ACCESS)
flags = (*eIt).m_long;
}
if ((mimeType == "message/rfc822-imap" || mimeType == "message/rfc822") &&
!(flags & 8)) {
(*it).items.append(name + "," + TQString::number(flags));
if ( mMailCheckProgressItem ) {
mMailCheckProgressItem->incCompletedItems();
mMailCheckProgressItem->updateProgress();
}
}
}
}
// debugging helper
//X static TQString flagsToString( int flags )
//X {
//X TQString str("(");
//X if ( flags & 4 ) {
//X str += "\\Flagged ";
//X }
//X if ( flags & 2 ) {
//X str += "\\Answered ";
//X }
//X if ( flags & 1 ) {
//X str += "\\Seen";
//X }
//X str += ")";
//X return str;
//X }
//-----------------------------------------------------------------------------
void KMFolderImap::flagsTotqStatus(KMMsgBase *msg, int flags, bool newMsg, int supportedFlags )
{
if ( !msg ) return;
// see imap4/imapinfo.h for the magic numbers
static const struct {
const int imapFlag;
const int kmFlag;
const bool standardFlag;
} imapFlagMap[] = {
{ 2, KMMsgStatusReplied, true },
{ 4, KMMsgStatusFlag, true },
{ 128, KMMsgStatusForwarded, false },
{ 256, KMMsgStatusTodo, false },
{ 512, KMMsgStatusWatched, false },
{ 1024, KMMsgStatusIgnored, false }
};
static const int numFlags = sizeof imapFlagMap / sizeof *imapFlagMap;
const KMMsgtqStatus oldtqStatus = msg->status();
for ( int i = 0; i < numFlags; ++i ) {
if ( ( (supportedFlags & imapFlagMap[i].imapFlag) == 0 && (supportedFlags & 64) == 0 )
&& !imapFlagMap[i].standardFlag ) {
continue;
}
if ( ((flags & imapFlagMap[i].imapFlag) > 0) != ((oldtqStatus & imapFlagMap[i].kmFlag) > 0) ) {
msg->toggletqStatus( imapFlagMap[i].kmFlag );
}
}
seenFlagTotqStatus( msg, flags, newMsg );
}
void KMFolderImap::seenFlagTotqStatus(KMMsgBase * msg, int flags, bool newMsg)
{
if ( !msg ) return;
const KMMsgtqStatus oldtqStatus = msg->status();
if ( (flags & 1) && (oldtqStatus & KMMsgStatusOld) == 0 )
msg->setqStatus( KMMsgStatusOld );
// In case the message does not have the seen flag set, override our local
// notion that it is read. Otherwise the count of unread messages and the
// number of messages which actually show up as read can go out of sync.
if ( msg->isOfUnknowntqStatus() || (!(flags&1) && !(oldtqStatus&(KMMsgStatusNew|KMMsgStatusUnread)) ) ) {
if (newMsg) {
if ( (oldtqStatus & KMMsgStatusNew) == 0 )
msg->setqStatus( KMMsgStatusNew );
} else {
if ( (oldtqStatus & KMMsgStatusUnread) == 0 )
msg->setqStatus( KMMsgStatusUnread );
}
}
}
//-----------------------------------------------------------------------------
TQString KMFolderImap::statusToFlags(KMMsgtqStatus status, int supportedFlags)
{
TQString flags;
if (status & KMMsgStatusDeleted)
flags = "\\DELETED";
else {
if (status & KMMsgStatusOld || status & KMMsgStatusRead)
flags = "\\SEEN ";
if (status & KMMsgStatusReplied)
flags += "\\ANSWERED ";
if (status & KMMsgStatusFlag)
flags += "\\FLAGGED ";
// non standard flags
if ( (status & KMMsgStatusForwarded) && ((supportedFlags & 64) || (supportedFlags & 128)) )
flags += "$FORWARDED ";
if ( (status & KMMsgStatusTodo) && ((supportedFlags & 64) || (supportedFlags & 256)) )
flags += "$TODO ";
if ( (status & KMMsgStatusWatched) && ((supportedFlags & 64) || (supportedFlags & 512)) )
flags += "$WATCHED ";
if ( (status & KMMsgStatusIgnored) && ((supportedFlags & 64) || (supportedFlags & 1024)) )
flags += "$IGNORED ";
}
return flags.simplifyWhiteSpace();
}
//-------------------------------------------------------------
void
KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
{
if ( !msg || msg->transferInProgress() ||
!msg->tqparent() || msg->tqparent()->folderType() != KMFolderTypeImap )
return;
KMAcctImap *account;
if ( !(account = static_cast<KMFolderImap*>(msg->storage())->account()) )
return;
account->ignoreJobsForMessage( msg );
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotGetMessagesData(KIO::Job * job, const TQByteArray & data)
{
if ( data.isEmpty() ) return; // optimization
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
(*it).cdata += TQCString(data, data.size() + 1);
int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
if ( pos == -1 ) {
// if we do not find the pattern in the complete string we will not find
// it in a substring.
return;
}
if (pos > 0)
{
int p = (*it).cdata.find("\r\nX-uidValidity:");
if (p != -1) setUidValidity((*it).cdata
.mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
int c = (*it).cdata.find("\r\nX-Count:");
if ( c != -1 )
{
bool ok;
int exists = (*it).cdata.mid( c+10,
(*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
if ( ok && exists < count() ) {
kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" <<
exists << ") then folder (" << count() << "), so reload" << endl;
open("getMessage");
reallyGetFolder( TQString() );
(*it).cdata.remove(0, pos);
return;
} else if ( ok ) {
int delta = exists - count();
if ( mMailCheckProgressItem ) {
mMailCheckProgressItem->setTotalItems( delta );
}
}
}
(*it).cdata.remove(0, pos);
}
pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
int flags;
while (pos >= 0)
{
KMMessage *msg = new KMMessage;
msg->setComplete( false );
msg->setReadyToShow( false );
// nothing between the boundaries, older UWs do that
if ( pos != 14 ) {
msg->fromString( (*it).cdata.mid(16, pos - 16) );
flags = msg->headerField("X-Flags").toInt();
ulong uid = msg->UID();
KMMsgMetaData *md = 0;
if ( mUidMetaDataMap.find( uid ) ) {
md = mUidMetaDataMap[uid];
}
ulong serNum = 0;
if ( md ) {
serNum = md->serNum();
}
bool ok = true;
if ( uid <= lastUid() && serNum > 0 ) {
// the UID is already known so no need to create it
ok = false;
}
// deleted flag
if ( flags & 8 )
ok = false;
if ( !ok ) {
delete msg;
msg = 0;
} else {
if ( serNum > 0 ) {
// assign the sernum from the cache
msg->setMsgSerNum( serNum );
}
// Transfer the status, if it is cached.
if ( md ) {
msg->setqStatus( md->status() );
} else if ( !account()->hasCapability("uidplus") ) {
// see if we have cached the msgIdMD5 and get the status +
// serial number from there
TQString id = msg->msgIdMD5();
if ( mMetaDataMap.find( id ) ) {
md = mMetaDataMap[id];
msg->setqStatus( md->status() );
if ( md->serNum() != 0 && serNum == 0 ) {
msg->setMsgSerNum( md->serNum() );
}
mMetaDataMap.remove( id );
delete md;
}
}
KMFolderMbox::addMsg(msg, 0);
// Merge with the flags from the server.
flagsTotqStatus((KMMsgBase*)msg, flags, true, mUploadAllFlags ? 31 : mPermanentFlags);
// set the correct size
msg->setMsgSizeServer( msg->headerField("X-Length").toUInt() );
msg->setUID(uid);
if ( msg->getMsgSerNum() > 0 ) {
saveMsgMetaData( msg );
}
// Filter messages that have arrived in the inbox folder
if ( folder()->isSystemFolder() && imapPath() == "/INBOX/"
&& kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( account()->id() ) )
account()->execFilters( msg->getMsgSerNum() );
if ( count() > 1 ) {
unGetMsg(count() - 1);
}
mLastUid = uid;
if ( mMailCheckProgressItem ) {
mMailCheckProgressItem->incCompletedItems();
mMailCheckProgressItem->updateProgress();
}
}
}
(*it).cdata.remove(0, pos);
(*it).done++;
pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
} // while
}
//-------------------------------------------------------------
FolderJob*
KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
KMFolder *folder, TQString partSpecifier,
const AttachmentStrategy *as ) const
{
KMFolderImap* kmfi = folder? dynamic_cast<KMFolderImap*>(folder->storage()) : 0;
if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
account() && account()->loadOnDemand() &&
( msg->msgSizeServer() > 5000 || msg->msgSizeServer() == 0 ) &&
( msg->signatureState() == KMMsgNotSigned ||
msg->signatureState() == KMMsgSignatureStateUnknown ) &&
( msg->encryptionState() == KMMsgNotEncrypted ||
msg->encryptionState() == KMMsgEncryptionStateUnknown ) )
{
// load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
// this is not activated for small or signed messages
ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
job->start();
ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
job2->start();
job->setParentFolder( this );
return job;
} else {
// download complete message or part (attachment)
if ( partSpecifier == "STRUCTURE" ) // hide from outside
partSpecifier = TQString();
ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
job->setParentFolder( this );
return job;
}
}
//-------------------------------------------------------------
FolderJob*
KMFolderImap::doCreateJob( TQPtrList<KMMessage>& msgList, const TQString& sets,
FolderJob::JobType jt, KMFolder *folder ) const
{
KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder->storage());
ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
job->setParentFolder( this );
return job;
}
//-----------------------------------------------------------------------------
void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
{
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
if (job->error()) {
account()->handleJobError( job, i18n("Error while retrieving messages.") );
finishMailCheck( "getMessage", imapNoInformation );
return;
}
if (lastSet) {
finishMailCheck( "getMessage", imapFinished );
account()->removeJob(it);
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
{
getMessagesResult(job, true);
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
{
getMessagesResult(job, false);
}
//-----------------------------------------------------------------------------
void KMFolderImap::createFolder(const TQString &name, const TQString& parentPath,
bool askUser)
{
kdDebug(5006) << "KMFolderImap::createFolder - name=" << name << ",tqparent=" <<
parentPath << ",askUser=" << askUser << endl;
if ( account()->makeConnection() != ImapAccountBase::Connected ) {
kdWarning(5006) << "KMFolderImap::createFolder - got no connection" << endl;
return;
}
KURL url = account()->getUrl();
TQString tqparent = ( parentPath.isEmpty() ? imapPath() : parentPath );
TQString path = account()->createImapPath( tqparent, name );
if ( askUser ) {
path += "/;INFO=ASKUSER";
}
url.setPath( path );
KIO::SimpleJob *job = KIO::mkdir(url);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd( url.url(), folder() );
jd.items = name;
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
this, TQT_SLOT(slotCreateFolderResult(KIO::Job *)));
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
{
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
TQString name;
if ( it.data().items.count() > 0 )
name = it.data().items.first();
if (job->error())
{
if ( job->error() == KIO::ERR_COULD_NOT_MKDIR ) {
// Creating a folder failed, remove it from the tree.
account()->listDirectory( );
}
account()->handleJobError( job, i18n("Error while creating a folder.") );
emit folderCreationResult( name, false );
} else {
listDirectory();
account()->removeJob(job);
emit folderCreationResult( name, true );
}
}
//-----------------------------------------------------------------------------
static TQTextCodec *sUtf7Codec = 0;
TQTextCodec * KMFolderImap::utf7Codec()
{
if (!sUtf7Codec) sUtf7Codec = TQTextCodec::codecForName("utf-7");
return sUtf7Codec;
}
//-----------------------------------------------------------------------------
TQString KMFolderImap::encodeFileName(const TQString &name)
{
TQString result = utf7Codec()->fromUnicode(name);
return KURL::encode_string_no_slash(result);
}
//-----------------------------------------------------------------------------
TQString KMFolderImap::decodeFileName(const TQString &name)
{
TQString result = KURL::decode_string(name);
return utf7Codec()->toUnicode(result.latin1());
}
//-----------------------------------------------------------------------------
bool KMFolderImap::autoExpunge()
{
if (account())
return account()->autoExpunge();
return false;
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotSimpleData(KIO::Job * job, const TQByteArray & data)
{
if ( data.isEmpty() ) return; // optimization
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
TQBuffer buff((*it).data);
buff.open(IO_WriteOnly | IO_Append);
buff.writeBlock(data.data(), data.size());
buff.close();
}
//-----------------------------------------------------------------------------
void KMFolderImap::deleteMessage(KMMessage * msg)
{
mUidMetaDataMap.remove( msg->UID() );
mMetaDataMap.remove( msg->msgIdMD5() );
KURL url = account()->getUrl();
KMFolderImap *msg_tqparent = static_cast<KMFolderImap*>(msg->storage());
ulong uid = msg->UID();
/* If the uid is empty the delete job below will nuke all mail in the
folder, so we better safeguard against that. See ::expungeFolder, as
to why. :( */
if ( uid == 0 ) {
kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
"an empty UID. Aborting." << endl;
return;
}
url.setPath(msg_tqparent->imapPath() + ";UID=" + TQString::number(uid) );
if ( account()->makeConnection() != ImapAccountBase::Connected )
return;
KIO::SimpleJob *job = KIO::file_delete(url, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd( url.url(), 0 );
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
account(), TQT_SLOT(slotSimpleResult(KIO::Job *)));
}
void KMFolderImap::deleteMessage(const TQPtrList<KMMessage>& msgList)
{
TQPtrListIterator<KMMessage> it( msgList );
KMMessage *msg;
while ( (msg = it.current()) != 0 ) {
++it;
mUidMetaDataMap.remove( msg->UID() );
mMetaDataMap.remove( msg->msgIdMD5() );
}
TQValueList<ulong> uids;
getUids(msgList, uids);
TQStringList sets = makeSets(uids);
KURL url = account()->getUrl();
KMFolderImap *msg_tqparent = static_cast<KMFolderImap*>(msgList.getFirst()->storage());
for ( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
{
TQString uid = *it;
// Don't delete with no uid, that nukes the folder. Should not happen, but
// better safe than sorry.
if ( uid.isEmpty() ) continue;
url.setPath(msg_tqparent->imapPath() + ";UID=" + uid);
if ( account()->makeConnection() != ImapAccountBase::Connected )
return;
KIO::SimpleJob *job = KIO::file_delete(url, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd( url.url(), 0 );
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
account(), TQT_SLOT(slotSimpleResult(KIO::Job *)));
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::setqStatus(int idx, KMMsgtqStatus status, bool toggle)
{
TQValueList<int> ids; ids.append(idx);
setqStatus(ids, status, toggle);
}
void KMFolderImap::setqStatus(TQValueList<int>& _ids, KMMsgtqStatus status, bool toggle)
{
FolderStorage::setqStatus(_ids, status, toggle);
TQValueList<int> ids;
if ( mUploadAllFlags ) {
kdDebug(5006) << k_funcinfo << "Migrating all flags to the server" << endl;
ids.clear();
for ( int i = 0; i < count(); ++i )
ids << i;
mUploadAllFlags = false;
} else {
ids = _ids;
}
/* The status has been already set in the local index. Update the flags on
* the server. To avoid doing that for each message individually, group them
* by the status string they will be assigned and make sets for each of those
* groups of mails. This is necessary because the imap kio_slave status job
* does not append flags but overwrites them. Example:
*
* 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
* this method with a list of uids. The 2 important mails need to get the string
* \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
* of those and sort them, so the server can handle them efficiently. */
if ( mReadOnly ) { // mUserRights is not available here
// FIXME duplicated code in KMFolderCachedImap
TQValueList<ulong> seenUids, unseenUids;
for ( TQValueList<int>::ConstIterator it = ids.constBegin(); it != ids.constEnd(); ++it ) {
KMMessage *msg = 0;
bool unget = !isMessage(*it);
msg = getMsg(*it);
if (!msg) continue;
if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
seenUids.append( msg->UID() );
else
unseenUids.append( msg->UID() );
if (unget) unGetMsg(*it);
}
if ( !seenUids.isEmpty() ) {
TQStringList sets = KMFolderImap::makeSets( seenUids, true );
for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
TQString imappath = imapPath() + ";UID=" + ( *it );
account()->setImapSeentqStatus( folder(), imappath, true );
}
}
if ( !unseenUids.isEmpty() ) {
TQStringList sets = KMFolderImap::makeSets( unseenUids, true );
for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
TQString imappath = imapPath() + ";UID=" + ( *it );
account()->setImapSeentqStatus( folder(), imappath, false );
}
}
return;
}
TQMap< TQString, TQStringList > groups;
for ( TQValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
KMMessage *msg = 0;
bool unget = !isMessage(*it);
msg = getMsg(*it);
if (!msg) continue;
TQString flags = statusToFlags(msg->status(), mPermanentFlags);
// Collect uids for each type of flags.
groups[flags].append(TQString::number(msg->UID()));
if (unget) unGetMsg(*it);
}
TQMapIterator< TQString, TQStringList > dit;
for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
TQCString flags = dit.key().latin1();
TQStringList sets = makeSets( (*dit), true );
// Send off a status setting job for each set.
for ( TQStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
TQString imappath = imapPath() + ";UID=" + ( *slit );
account()->setImaptqStatus(folder(), imappath, flags);
}
}
if ( mContentState == imapListingInProgress ) {
// we're currently get'ing this folder
// to make sure that we get the latest flags abort the current listing and
// create a new one
kdDebug(5006) << "Set status during folder listing, restarting listing." << endl;
disconnect(this, TQT_SLOT(slotListFolderResult(KIO::Job *)));
quiet( false );
reallyGetFolder( TQString() );
}
}
//-----------------------------------------------------------------------------
TQStringList KMFolderImap::makeSets(const TQStringList& uids, bool sort)
{
TQValueList<ulong> tmp;
for ( TQStringList::ConstIterator it = uids.begin(); it != uids.end(); ++it )
tmp.append( (*it).toInt() );
return makeSets(tmp, sort);
}
TQStringList KMFolderImap::makeSets( TQValueList<ulong>& uids, bool sort )
{
TQStringList sets;
TQString set;
if (uids.size() == 1)
{
sets.append(TQString::number(uids.first()));
return sets;
}
if (sort) qHeapSort(uids);
ulong last = 0;
// needed to make a uid like 124 instead of 124:124
bool inserted = false;
/* iterate over uids and build sets like 120:122,124,126:150 */
for ( TQValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
{
if (it == uids.begin() || set.isEmpty()) {
set = TQString::number(*it);
inserted = true;
} else
{
if (last+1 != *it)
{
// end this range
if (inserted)
set += ',' + TQString::number(*it);
else
set += ':' + TQString::number(last) + ',' + TQString::number(*it);
inserted = true;
if (set.length() > 100)
{
// just in case the server has a problem with longer lines..
sets.append(set);
set = "";
}
} else {
inserted = false;
}
}
last = *it;
}
// last element
if (!inserted)
set += ':' + TQString::number(uids.last());
if (!set.isEmpty()) sets.append(set);
return sets;
}
//-----------------------------------------------------------------------------
void KMFolderImap::getUids(TQValueList<int>& ids, TQValueList<ulong>& uids)
{
KMMsgBase *msg = 0;
// get the uids
for ( TQValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
{
msg = getMsgBase(*it);
if (!msg) continue;
uids.append(msg->UID());
}
}
void KMFolderImap::getUids(const TQPtrList<KMMessage>& msgList, TQValueList<ulong>& uids)
{
KMMessage *msg = 0;
TQPtrListIterator<KMMessage> it( msgList );
while ( (msg = it.current()) != 0 ) {
++it;
if ( msg->UID() > 0 ) {
uids.append( msg->UID() );
}
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
{
aFolder->setNeedsCompacting(false);
KURL url = account()->getUrl();
url.setPath(aFolder->imapPath() + ";UID=*");
if ( account()->makeConnection() != ImapAccountBase::Connected )
return;
KIO::SimpleJob *job = KIO::file_delete(url, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd( url.url(), 0 );
jd.quiet = quiet;
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
account(), TQT_SLOT(slotSimpleResult(KIO::Job *)));
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotProcessNewMail( int errorCode, const TQString &errorMsg )
{
Q_UNUSED( errorMsg );
disconnect( account(), TQT_SIGNAL( connectionResult(int, const TQString&) ),
this, TQT_SLOT( slotProcessNewMail(int, const TQString&) ) );
if ( !errorCode )
processNewMail( false );
else
emit numUnreadMsgsChanged( folder() );
}
//-----------------------------------------------------------------------------
bool KMFolderImap::processNewMail(bool)
{
// a little safety
if ( !account() ) {
kdDebug(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
return false;
}
if ( imapPath().isEmpty() ) {
kdDebug(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
// remove it locally
setAlreadyRemoved( true );
kmkernel->imapFolderMgr()->remove( folder() );
return false;
}
// check the connection
if ( account()->makeConnection() == ImapAccountBase::Error ) {
kdDebug(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
return false;
} else if ( account()->makeConnection() == ImapAccountBase::Connecting )
{
// wait
kdDebug(5006) << "KMFolderImap::processNewMail - waiting for connection: " << label() << endl;
connect( account(), TQT_SIGNAL( connectionResult(int, const TQString&) ),
this, TQT_SLOT( slotProcessNewMail(int, const TQString&) ) );
return true;
}
KURL url = account()->getUrl();
if (mReadOnly)
url.setPath(imapPath() + ";SECTION=UIDNEXT");
else
url.setPath(imapPath() + ";SECTION=UNSEEN");
mMailCheckProgressItem = ProgressManager::createProgressItem(
"MailCheckAccount" + account()->name(),
"MailCheck" + folder()->prettyURL(),
TQStyleSheet::escape( folder()->prettyURL() ),
i18n("updating message counts"),
false,
account()->useSSL() || account()->useTLS() );
KIO::SimpleJob *job = KIO::stat(url, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd(url.url(), folder() );
jd.cancellable = true;
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
TQT_SLOT(slotStatResult(KIO::Job *)));
return true;
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotStatResult(KIO::Job * job)
{
slotCompleteMailCheckProgress();
ImapAccountBase::JobIterator it = account()->findJob(job);
if ( it == account()->jobsEnd() ) return;
account()->removeJob(it);
if (job->error())
{
account()->handleJobError( job, i18n("Error while getting folder information.") );
} else {
KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
{
if ((*it).m_uds == KIO::UDS_SIZE)
{
if (mReadOnly)
{
mGuessedUnreadMsgs = -1;
mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
} else {
mGuessedUnreadMsgs = (*it).m_long;
}
}
}
}
}
//-----------------------------------------------------------------------------
int KMFolderImap::create()
{
readConfig();
mUnreadMsgs = -1;
return KMFolderMbox::create();
}
TQValueList<ulong> KMFolderImap::splitSets(const TQString uids)
{
TQValueList<ulong> uidlist;
// ex: 1205,1204,1203,1202,1236:1238
TQString buffer = TQString();
int setstart = -1;
// iterate over the uids
for (uint i = 0; i < uids.length(); i++)
{
TQChar chr = uids[i];
if (chr == ',')
{
if (setstart > -1)
{
// a range (uid:uid) was before
for (int j = setstart; j <= buffer.toInt(); j++)
{
uidlist.append(j);
}
setstart = -1;
} else {
// single uid
uidlist.append(buffer.toInt());
}
buffer = "";
} else if (chr == ':') {
// remember the start of the range
setstart = buffer.toInt();
buffer = "";
} else if (chr.category() == TQChar::Number_DecimalDigit) {
// digit
buffer += chr;
} else {
// ignore
}
}
// process the last data
if (setstart > -1)
{
for (int j = setstart; j <= buffer.toInt(); j++)
{
uidlist.append(j);
}
} else {
uidlist.append(buffer.toInt());
}
return uidlist;
}
//-----------------------------------------------------------------------------
int KMFolderImap::expungeContents()
{
// nuke the local cache
int rc = KMFolderMbox::expungeContents();
// set the deleted flag for all messages in the folder
KURL url = account()->getUrl();
url.setPath( imapPath() + ";UID=1:*");
if ( account()->makeConnection() == ImapAccountBase::Connected )
{
KIO::SimpleJob *job = KIO::file_delete(url, false);
KIO::Scheduler::assignJobToSlave(account()->slave(), job);
ImapAccountBase::jobData jd( url.url(), 0 );
jd.quiet = true;
account()->insertJob(job, jd);
connect(job, TQT_SIGNAL(result(KIO::Job *)),
account(), TQT_SLOT(slotSimpleResult(KIO::Job *)));
}
/* Is the below correct? If we are expunging (in the folder sense, not the imap sense),
why delete but not (imap-)expunge? Since the folder is not active there is no concept
of "leaving the folder", so the setting really has little to do with it. */
// if ( autoExpunge() )
expungeFolder(this, true);
getFolder();
return rc;
}
//-----------------------------------------------------------------------------
void
KMFolderImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState userRightsState )
{
mUserRights = userRights;
mUserRightsState = userRightsState;
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotCompleteMailCheckProgress()
{
if ( mMailCheckProgressItem ) {
mMailCheckProgressItem->setComplete();
mMailCheckProgressItem = 0;
emit numUnreadMsgsChanged( folder() );
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::setSubfolderState( imapState state )
{
mSubfolderState = state;
if ( state == imapNoInformation && folder()->child() )
{
// pass through to tqchildren
KMFolderNode* node;
TQPtrListIterator<KMFolderNode> it( *folder()->child() );
for ( ; (node = it.current()); )
{
++it;
if (node->isDir()) continue;
KMFolder *folder = static_cast<KMFolder*>(node);
static_cast<KMFolderImap*>(folder->storage())->setSubfolderState( state );
}
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::setIncludeInMailCheck( bool check )
{
bool changed = ( mCheckMail != check );
mCheckMail = check;
if ( changed )
account()->slotUpdateFolderList();
}
//-----------------------------------------------------------------------------
void KMFolderImap::setAlreadyRemoved( bool removed )
{
mAlreadyRemoved = removed;
if ( folder()->child() )
{
// pass through to childs
KMFolderNode* node;
TQPtrListIterator<KMFolderNode> it( *folder()->child() );
for ( ; (node = it.current()); )
{
++it;
if (node->isDir()) continue;
KMFolder *folder = static_cast<KMFolder*>(node);
static_cast<KMFolderImap*>(folder->storage())->setAlreadyRemoved( removed );
}
}
}
void KMFolderImap::slotCreatePendingFolders( int errorCode, const TQString& errorMsg )
{
Q_UNUSED( errorMsg );
disconnect( account(), TQT_SIGNAL( connectionResult( int, const TQString& ) ),
this, TQT_SLOT( slotCreatePendingFolders( int, const TQString& ) ) );
if ( !errorCode ) {
TQStringList::Iterator it = mFoldersPendingCreation.begin();
for ( ; it != mFoldersPendingCreation.end(); ++it ) {
createFolder( *it );
}
}
mFoldersPendingCreation.clear();
}
//-----------------------------------------------------------------------------
void KMFolderImap::search( const KMSearchPattern* pattern )
{
if ( !pattern || pattern->isEmpty() )
{
// not much to do here
TQValueList<TQ_UINT32> serNums;
emit searchResult( folder(), serNums, pattern, true );
return;
}
SearchJob* job = new SearchJob( this, account(), pattern );
connect( job, TQT_SIGNAL( searchDone( TQValueList<TQ_UINT32>, const KMSearchPattern*, bool ) ),
this, TQT_SLOT( slotSearchDone( TQValueList<TQ_UINT32>, const KMSearchPattern*, bool ) ) );
job->start();
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotSearchDone( TQValueList<TQ_UINT32> serNums,
const KMSearchPattern* pattern,
bool complete )
{
emit searchResult( folder(), serNums, pattern, complete );
}
//-----------------------------------------------------------------------------
void KMFolderImap::search( const KMSearchPattern* pattern, TQ_UINT32 serNum )
{
if ( !pattern || pattern->isEmpty() )
{
// not much to do here
emit searchDone( folder(), serNum, pattern, false );
return;
}
SearchJob* job = new SearchJob( this, account(), pattern, serNum );
connect( job, TQT_SIGNAL( searchDone( TQ_UINT32, const KMSearchPattern*, bool ) ),
this, TQT_SLOT( slotSearchDone( TQ_UINT32, const KMSearchPattern*, bool ) ) );
job->start();
}
//-----------------------------------------------------------------------------
void KMFolderImap::slotSearchDone( TQ_UINT32 serNum, const KMSearchPattern* pattern,
bool matches )
{
emit searchDone( folder(), serNum, pattern, matches );
}
//-----------------------------------------------------------------------------
bool KMFolderImap::isMoveable() const
{
return ( hasChildren() == HasNoChildren &&
!folder()->isSystemFolder() ) ? true : false;
}
//-----------------------------------------------------------------------------
ulong KMFolderImap::serNumForUID( ulong uid )
{
if ( mUidMetaDataMap.find( uid ) ) {
KMMsgMetaData *md = mUidMetaDataMap[uid];
return md->serNum();
} else {
kdDebug(5006) << "serNumForUID: unknown uid " << uid << endl;
return 0;
}
}
//-----------------------------------------------------------------------------
void KMFolderImap::saveMsgMetaData( KMMessage* msg, ulong uid )
{
if ( uid == 0 ) {
uid = msg->UID();
}
ulong serNum = msg->getMsgSerNum();
mUidMetaDataMap.replace( uid, new KMMsgMetaData(msg->status(), serNum) );
}
//-----------------------------------------------------------------------------
void KMFolderImap::setImapPath( const TQString& path )
{
if ( path.isEmpty() ) {
kdWarning(5006) << k_funcinfo << "ignoring empty path" << endl;
} else {
mImapPath = path;
}
}
void KMFolderImap::finishMailCheck( const char *dbg, imapState state )
{
quiet( false );
mContentState = state;
emit folderComplete( this, mContentState == imapFinished );
close(dbg);
}
bool KMFolderImap::canDeleteMessages() const
{
if ( isReadOnly() )
return false;
if ( mUserRightsState == KMail::ACLJobs::Ok && !(mUserRights & KMail::ACLJobs::Delete) )
return false;
return true;
}
#include "kmfolderimap.moc"