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.
1455 lines
52 KiB
1455 lines
52 KiB
/** -*- c++ -*-
|
|
* imapaccountbase.cpp
|
|
*
|
|
* Copyright (c) 2000-2002 Michael Haeckel <haeckel@kde.org>
|
|
* Copyright (c) 2002 Marc Mutz <mutz@kde.org>
|
|
*
|
|
* This file is based on work on pop3 and imap account implementations
|
|
* by Don Sanders <sanders@kde.org> and Michael Haeckel <haeckel@kde.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; 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 "imapaccountbase.h"
|
|
using KMail::SieveConfig;
|
|
|
|
#include "accountmanager.h"
|
|
using KMail::AccountManager;
|
|
#include "kmfolder.h"
|
|
#include "broadcaststatus.h"
|
|
using KPIM::BroadcastStatus;
|
|
#include "kmmainwin.h"
|
|
#include "kmfolderimap.h"
|
|
#include "kmmainwidget.h"
|
|
#include "kmmainwin.h"
|
|
#include "kmmsgpart.h"
|
|
#include "acljobs.h"
|
|
#include "kmfoldercachedimap.h"
|
|
#include "bodyvisitor.h"
|
|
using KMail::BodyVisitor;
|
|
#include "imapjob.h"
|
|
using KMail::ImapJob;
|
|
#include "protocols.h"
|
|
#include "progressmanager.h"
|
|
using KPIM::ProgressManager;
|
|
#include "kmfoldermgr.h"
|
|
#include "listjob.h"
|
|
|
|
#include <tdeapplication.h>
|
|
#include <kdebug.h>
|
|
#include <tdeconfig.h>
|
|
#include <tdelocale.h>
|
|
#include <tdemessagebox.h>
|
|
using TDEIO::MetaData;
|
|
#include <tdeio/passdlg.h>
|
|
using TDEIO::PasswordDialog;
|
|
#include <tdeio/scheduler.h>
|
|
#include <tdeio/slave.h>
|
|
#include <mimelib/bodypart.h>
|
|
#include <mimelib/body.h>
|
|
#include <mimelib/headers.h>
|
|
#include <mimelib/message.h>
|
|
//using TDEIO::Scheduler; // use FQN below
|
|
|
|
#include <tqregexp.h>
|
|
#include <tqstylesheet.h>
|
|
|
|
namespace KMail {
|
|
|
|
static const unsigned short int imapDefaultPort = 143;
|
|
|
|
//
|
|
//
|
|
// Ctor and Dtor
|
|
//
|
|
//
|
|
|
|
ImapAccountBase::ImapAccountBase( AccountManager * parent, const TQString & name, uint id )
|
|
: NetworkAccount( parent, name, id ),
|
|
mIdleTimer( 0, "mIdleTimer" ),
|
|
mNoopTimer( 0, "mNoopTimer" ),
|
|
mTotal( 0 ),
|
|
mCountUnread( 0 ),
|
|
mCountLastUnread( 0 ),
|
|
mAutoExpunge( true ),
|
|
mHiddenFolders( false ),
|
|
mOnlySubscribedFolders( false ),
|
|
mOnlyLocallySubscribedFolders( false ),
|
|
mLoadOnDemand( true ),
|
|
mListOnlyOpenFolders( false ),
|
|
mProgressEnabled( false ),
|
|
mErrorDialogIsActive( false ),
|
|
mPasswordDialogIsActive( false ),
|
|
mACLSupport( true ),
|
|
mAnnotationSupport( true ),
|
|
mQuotaSupport( true ),
|
|
mSlaveConnected( false ),
|
|
mSlaveConnectionError( false ),
|
|
mCheckingSingleFolder( false ),
|
|
mListDirProgressItem( 0 )
|
|
{
|
|
mPort = imapDefaultPort;
|
|
mBodyPartList.setAutoDelete(true);
|
|
TDEIO::Scheduler::connect(TQT_SIGNAL(slaveError(TDEIO::Slave *, int, const TQString &)),
|
|
this, TQT_SLOT(slotSchedulerSlaveError(TDEIO::Slave *, int, const TQString &)));
|
|
TDEIO::Scheduler::connect(TQT_SIGNAL(slaveConnected(TDEIO::Slave *)),
|
|
this, TQT_SLOT(slotSchedulerSlaveConnected(TDEIO::Slave *)));
|
|
connect(&mNoopTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotNoopTimeout()));
|
|
connect(&mIdleTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotIdleTimeout()));
|
|
}
|
|
|
|
ImapAccountBase::~ImapAccountBase() {
|
|
kdWarning( mSlave, 5006 )
|
|
<< "slave should have been destroyed by subclass!" << endl;
|
|
}
|
|
|
|
void ImapAccountBase::init() {
|
|
mAutoExpunge = true;
|
|
mHiddenFolders = false;
|
|
mOnlySubscribedFolders = false;
|
|
mOnlyLocallySubscribedFolders = false;
|
|
mLoadOnDemand = true;
|
|
mListOnlyOpenFolders = false;
|
|
mProgressEnabled = false;
|
|
}
|
|
|
|
void ImapAccountBase::pseudoAssign( const KMAccount * a ) {
|
|
NetworkAccount::pseudoAssign( a );
|
|
|
|
const ImapAccountBase * i = dynamic_cast<const ImapAccountBase*>( a );
|
|
if ( !i ) return;
|
|
|
|
setAutoExpunge( i->autoExpunge() );
|
|
setHiddenFolders( i->hiddenFolders() );
|
|
setOnlySubscribedFolders( i->onlySubscribedFolders() );
|
|
setOnlyLocallySubscribedFolders( i->onlyLocallySubscribedFolders() );
|
|
setLoadOnDemand( i->loadOnDemand() );
|
|
setListOnlyOpenFolders( i->listOnlyOpenFolders() );
|
|
setNamespaces( i->namespaces() );
|
|
setNamespaceToDelimiter( i->namespaceToDelimiter() );
|
|
localBlacklistFromStringList( i->locallyBlacklistedFolders() );
|
|
}
|
|
|
|
unsigned short int ImapAccountBase::defaultPort() const {
|
|
return imapDefaultPort;
|
|
}
|
|
|
|
TQString ImapAccountBase::protocol() const {
|
|
return useSSL() ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Getters and Setters
|
|
//
|
|
//
|
|
|
|
void ImapAccountBase::setAutoExpunge( bool expunge ) {
|
|
mAutoExpunge = expunge;
|
|
}
|
|
|
|
void ImapAccountBase::setHiddenFolders( bool show ) {
|
|
mHiddenFolders = show;
|
|
}
|
|
|
|
void ImapAccountBase::setOnlySubscribedFolders( bool show ) {
|
|
mOnlySubscribedFolders = show;
|
|
}
|
|
|
|
void ImapAccountBase::setOnlyLocallySubscribedFolders( bool show ) {
|
|
mOnlyLocallySubscribedFolders = show;
|
|
}
|
|
|
|
void ImapAccountBase::setLoadOnDemand( bool load ) {
|
|
mLoadOnDemand = load;
|
|
}
|
|
|
|
void ImapAccountBase::setListOnlyOpenFolders( bool only ) {
|
|
mListOnlyOpenFolders = only;
|
|
}
|
|
|
|
//
|
|
//
|
|
// read/write config
|
|
//
|
|
//
|
|
|
|
void ImapAccountBase::readConfig( /*const*/ TDEConfig/*Base*/ & config ) {
|
|
NetworkAccount::readConfig( config );
|
|
|
|
setAutoExpunge( config.readBoolEntry( "auto-expunge", false ) );
|
|
setHiddenFolders( config.readBoolEntry( "hidden-folders", false ) );
|
|
setOnlySubscribedFolders( config.readBoolEntry( "subscribed-folders", false ) );
|
|
setOnlyLocallySubscribedFolders( config.readBoolEntry( "locally-subscribed-folders", false ) );
|
|
setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) );
|
|
setListOnlyOpenFolders( config.readBoolEntry( "listOnlyOpenFolders", false ) );
|
|
mCapabilities = config.readListEntry( "capabilities", TQStringList() );
|
|
// read namespaces
|
|
nsMap map;
|
|
TQStringList list = config.readListEntry( TQString::number( PersonalNS ) );
|
|
if ( !list.isEmpty() )
|
|
map[PersonalNS] = list.gres( "\"", "" );
|
|
list = config.readListEntry( TQString::number( OtherUsersNS ) );
|
|
if ( !list.isEmpty() )
|
|
map[OtherUsersNS] = list.gres( "\"", "" );
|
|
list = config.readListEntry( TQString::number( SharedNS ) );
|
|
if ( !list.isEmpty() )
|
|
map[SharedNS] = list.gres( "\"", "" );
|
|
setNamespaces( map );
|
|
// read namespace - delimiter
|
|
namespaceDelim entries = config.entryMap( config.group() );
|
|
namespaceDelim namespaceToDelimiter;
|
|
for ( namespaceDelim::ConstIterator it = entries.begin();
|
|
it != entries.end(); ++it ) {
|
|
if ( it.key().startsWith( "Namespace:" ) ) {
|
|
TQString key = it.key().right( it.key().length() - 10 );
|
|
namespaceToDelimiter[key] = it.data();
|
|
}
|
|
}
|
|
setNamespaceToDelimiter( namespaceToDelimiter );
|
|
mOldPrefix = config.readEntry( "prefix" );
|
|
if ( !mOldPrefix.isEmpty() ) {
|
|
makeConnection();
|
|
}
|
|
localBlacklistFromStringList( config.readListEntry( "locallyUnsubscribedFolders" ) );
|
|
}
|
|
|
|
void ImapAccountBase::writeConfig( TDEConfig/*Base*/ & config ) /*const*/ {
|
|
NetworkAccount::writeConfig( config );
|
|
|
|
config.writeEntry( "auto-expunge", autoExpunge() );
|
|
config.writeEntry( "hidden-folders", hiddenFolders() );
|
|
config.writeEntry( "subscribed-folders", onlySubscribedFolders() );
|
|
config.writeEntry( "locally-subscribed-folders", onlyLocallySubscribedFolders() );
|
|
config.writeEntry( "loadondemand", loadOnDemand() );
|
|
config.writeEntry( "listOnlyOpenFolders", listOnlyOpenFolders() );
|
|
config.writeEntry( "capabilities", mCapabilities );
|
|
TQString data;
|
|
for ( nsMap::Iterator it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) {
|
|
if ( !it.data().isEmpty() ) {
|
|
data = "\"" + it.data().join("\",\"") + "\"";
|
|
config.writeEntry( TQString::number( it.key() ), data );
|
|
}
|
|
}
|
|
TQString key;
|
|
for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin();
|
|
it != mNamespaceToDelimiter.end(); ++it ) {
|
|
key = "Namespace:" + it.key();
|
|
config.writeEntry( key, it.data() );
|
|
}
|
|
config.writeEntry( "locallyUnsubscribedFolders", locallyBlacklistedFolders() );
|
|
}
|
|
|
|
//
|
|
//
|
|
// Network processing
|
|
//
|
|
//
|
|
|
|
MetaData ImapAccountBase::slaveConfig() const {
|
|
MetaData m = NetworkAccount::slaveConfig();
|
|
|
|
m.insert( "auth", auth() );
|
|
if ( autoExpunge() )
|
|
m.insert( "expunge", "auto" );
|
|
|
|
return m;
|
|
}
|
|
|
|
ImapAccountBase::ConnectionState ImapAccountBase::makeConnection()
|
|
{
|
|
if ( mSlave && mSlaveConnected ) {
|
|
return Connected;
|
|
}
|
|
if ( mPasswordDialogIsActive ) return Connecting;
|
|
|
|
if( mAskAgain || ( ( passwd().isEmpty() || login().isEmpty() ) &&
|
|
auth() != "GSSAPI" ) ) {
|
|
|
|
Q_ASSERT( !mSlave ); // disconnected on 'wrong login' error already, or first try
|
|
TQString log = login();
|
|
TQString pass = passwd();
|
|
// We init "store" to true to indicate that we want to have the
|
|
// "keep password" checkbox. Then, we set [Passwords]Keep to
|
|
// storePasswd(), so that the checkbox in the dialog will be
|
|
// init'ed correctly:
|
|
TDEConfigGroup passwords( TDEGlobal::config(), "Passwords" );
|
|
passwords.writeEntry( "Keep", storePasswd() );
|
|
TQString msg = i18n("You need to supply a username and a password to "
|
|
"access this mailbox.");
|
|
mPasswordDialogIsActive = true;
|
|
|
|
PasswordDialog dlg( msg, log, true /* store pw */, true, KMKernel::self()->mainWin() );
|
|
dlg.setPlainCaption( i18n("Authorization Dialog") );
|
|
dlg.addCommentLine( i18n("Account:"), name() );
|
|
int ret = dlg.exec();
|
|
if (ret != TQDialog::Accepted ) {
|
|
mPasswordDialogIsActive = false;
|
|
mAskAgain = false;
|
|
emit connectionResult( TDEIO::ERR_USER_CANCELED, TQString() );
|
|
return Error;
|
|
}
|
|
mPasswordDialogIsActive = false;
|
|
// The user has been given the chance to change login and
|
|
// password, so copy both from the dialog:
|
|
setPasswd( dlg.password(), dlg.keepPassword() );
|
|
setLogin( dlg.username() );
|
|
mAskAgain = false;
|
|
}
|
|
// already waiting for a connection?
|
|
if ( mSlave && !mSlaveConnected ) return Connecting;
|
|
|
|
mSlaveConnected = false;
|
|
mSlave = TDEIO::Scheduler::getConnectedSlave( getUrl(), slaveConfig() );
|
|
if ( !mSlave ) {
|
|
KMessageBox::error(0, i18n("Could not start process for %1.")
|
|
.arg( getUrl().protocol() ) );
|
|
return Error;
|
|
}
|
|
if ( mSlave->isConnected() ) {
|
|
slotSchedulerSlaveConnected( mSlave );
|
|
return Connected;
|
|
}
|
|
|
|
return Connecting;
|
|
}
|
|
|
|
bool ImapAccountBase::handleJobError( TDEIO::Job *job, const TQString& context, bool abortSync )
|
|
{
|
|
JobIterator it = findJob( job );
|
|
if ( it != jobsEnd() && (*it).progressItem )
|
|
{
|
|
(*it).progressItem->setComplete();
|
|
(*it).progressItem = 0;
|
|
}
|
|
return handleError( job->error(), job->errorText(), job, context, abortSync );
|
|
}
|
|
|
|
// Called when we're really all done.
|
|
void ImapAccountBase::postProcessNewMail( bool showStatusMsg ) {
|
|
setCheckingMail(false);
|
|
int newMails = 0;
|
|
if ( mCountUnread > 0 && mCountUnread > mCountLastUnread ) {
|
|
newMails = mCountUnread - mCountLastUnread;
|
|
mCountLastUnread = mCountUnread;
|
|
mCountUnread = 0;
|
|
checkDone( true, CheckOK );
|
|
} else {
|
|
mCountUnread = 0;
|
|
checkDone( false, CheckOK );
|
|
}
|
|
if ( showStatusMsg )
|
|
BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
|
|
name(), newMails);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::changeSubscription( bool subscribe, const TQString& imapPath, bool quiet )
|
|
{
|
|
// change the subscription of the folder
|
|
KURL url = getUrl();
|
|
url.setPath(imapPath);
|
|
|
|
TQByteArray packedArgs;
|
|
TQDataStream stream( packedArgs, IO_WriteOnly);
|
|
|
|
if (subscribe)
|
|
stream << (int) 'u' << url;
|
|
else
|
|
stream << (int) 'U' << url;
|
|
|
|
// create the KIO-job
|
|
if ( makeConnection() != Connected )
|
|
return;// ## doesn't handle Connecting
|
|
TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false);
|
|
TDEIO::Scheduler::assignJobToSlave(mSlave, job);
|
|
jobData jd( url.url(), NULL );
|
|
// a bit of a hack to save one slot
|
|
if (subscribe) jd.onlySubscribed = true;
|
|
else jd.onlySubscribed = false;
|
|
jd.quiet = quiet;
|
|
insertJob(job, jd);
|
|
|
|
connect(job, TQT_SIGNAL(result(TDEIO::Job *)),
|
|
TQT_SLOT(slotSubscriptionResult(TDEIO::Job *)));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotSubscriptionResult( TDEIO::Job * job )
|
|
{
|
|
// result of a subscription-job
|
|
JobIterator it = findJob( job );
|
|
if ( it == jobsEnd() ) return;
|
|
bool onlySubscribed = (*it).onlySubscribed;
|
|
TQString path = static_cast<TDEIO::SimpleJob*>(job)->url().path();
|
|
if (job->error())
|
|
{
|
|
if ( !(*it).quiet )
|
|
handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' );
|
|
emit subscriptionChangeFailed( job->errorString() );
|
|
// ## emit subscriptionChanged here in case anyone needs it to support continue/cancel
|
|
}
|
|
else
|
|
{
|
|
emit subscriptionChanged( path, onlySubscribed );
|
|
if (mSlave) removeJob(job);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// TODO imapPath can be removed once parent can be a KMFolderImapBase or whatever
|
|
void ImapAccountBase::getUserRights( KMFolder* parent, const TQString& imapPath )
|
|
{
|
|
// There isn't much point in asking the server about a user's rights on his own inbox,
|
|
// it might not be the effective permissions (at least with Cyrus, one can admin his own inbox,
|
|
// even after a SETACL that removes the admin permissions. Other imap servers apparently
|
|
// don't even allow removing one's own admin permission, so this code won't hurt either).
|
|
if ( imapPath == "/INBOX/" ) {
|
|
if ( parent->folderType() == KMFolderTypeImap )
|
|
static_cast<KMFolderImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok );
|
|
else if ( parent->folderType() == KMFolderTypeCachedImap )
|
|
static_cast<KMFolderCachedImap*>( parent->storage() )->setUserRights( ACLJobs::All, ACLJobs::Ok );
|
|
emit receivedUserRights( parent ); // warning, you need to connect first to get that one
|
|
return;
|
|
}
|
|
|
|
KURL url = getUrl();
|
|
url.setPath(imapPath);
|
|
|
|
ACLJobs::GetUserRightsJob* job = ACLJobs::getUserRights( mSlave, url );
|
|
|
|
jobData jd( url.url(), parent );
|
|
jd.cancellable = true;
|
|
insertJob(job, jd);
|
|
|
|
connect(job, TQT_SIGNAL(result(TDEIO::Job *)),
|
|
TQT_SLOT(slotGetUserRightsResult(TDEIO::Job *)));
|
|
}
|
|
|
|
void ImapAccountBase::slotGetUserRightsResult( TDEIO::Job* _job )
|
|
{
|
|
ACLJobs::GetUserRightsJob* job = static_cast<ACLJobs::GetUserRightsJob *>( _job );
|
|
JobIterator it = findJob( job );
|
|
if ( it == jobsEnd() ) return;
|
|
|
|
KMFolder* folder = (*it).parent;
|
|
if ( job->error() ) {
|
|
if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) // that's when the imap server doesn't support ACLs
|
|
mACLSupport = false;
|
|
else
|
|
kdWarning(5006) << "slotGetUserRightsResult: " << job->errorString() << endl;
|
|
} else {
|
|
#ifndef NDEBUG
|
|
//kdDebug(5006) << "User Rights: " << ACLJobs::permissionsToString( job->permissions() ) << endl;
|
|
#endif
|
|
}
|
|
// Store the permissions
|
|
if ( folder->folderType() == KMFolderTypeImap )
|
|
static_cast<KMFolderImap*>( folder->storage() )->setUserRights( job->permissions(),
|
|
job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok );
|
|
else if ( folder->folderType() == KMFolderTypeCachedImap )
|
|
static_cast<KMFolderCachedImap*>( folder->storage() )->setUserRights( job->permissions(),
|
|
job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok );
|
|
|
|
if (mSlave) removeJob(job);
|
|
emit receivedUserRights( folder );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::getACL( KMFolder* parent, const TQString& imapPath )
|
|
{
|
|
KURL url = getUrl();
|
|
url.setPath(imapPath);
|
|
|
|
ACLJobs::GetACLJob* job = ACLJobs::getACL( mSlave, url );
|
|
jobData jd( url.url(), parent );
|
|
jd.cancellable = true;
|
|
insertJob(job, jd);
|
|
|
|
connect(job, TQT_SIGNAL(result(TDEIO::Job *)),
|
|
TQT_SLOT(slotGetACLResult(TDEIO::Job *)));
|
|
}
|
|
|
|
void ImapAccountBase::slotGetACLResult( TDEIO::Job* _job )
|
|
{
|
|
ACLJobs::GetACLJob* job = static_cast<ACLJobs::GetACLJob *>( _job );
|
|
JobIterator it = findJob( job );
|
|
if ( it == jobsEnd() ) return;
|
|
|
|
KMFolder* folder = (*it).parent;
|
|
emit receivedACL( folder, job, job->entries() );
|
|
if (mSlave) removeJob(job);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Do not remove imapPath, FolderDiaQuotaTab needs to call this with parent==0.
|
|
void ImapAccountBase::getStorageQuotaInfo( KMFolder* parent, const TQString& imapPath )
|
|
{
|
|
if ( !mSlave ) return;
|
|
KURL url = getUrl();
|
|
url.setPath(imapPath);
|
|
|
|
QuotaJobs::GetStorageQuotaJob* job = QuotaJobs::getStorageQuota( mSlave, url );
|
|
jobData jd( url.url(), parent );
|
|
jd.cancellable = true;
|
|
insertJob(job, jd);
|
|
|
|
connect(job, TQT_SIGNAL(result(TDEIO::Job *)),
|
|
TQT_SLOT(slotGetStorageQuotaInfoResult(TDEIO::Job *)));
|
|
}
|
|
|
|
void ImapAccountBase::slotGetStorageQuotaInfoResult( TDEIO::Job* _job )
|
|
{
|
|
QuotaJobs::GetStorageQuotaJob* job = static_cast<QuotaJobs::GetStorageQuotaJob *>( _job );
|
|
JobIterator it = findJob( job );
|
|
if ( it == jobsEnd() ) return;
|
|
if ( job->error() && job->error() == TDEIO::ERR_UNSUPPORTED_ACTION )
|
|
setHasNoQuotaSupport();
|
|
|
|
KMFolder* folder = (*it).parent; // can be 0
|
|
emit receivedStorageQuotaInfo( folder, job, job->storageQuotaInfo() );
|
|
if (mSlave) removeJob(job);
|
|
}
|
|
|
|
void ImapAccountBase::slotNoopTimeout()
|
|
{
|
|
if ( mSlave ) {
|
|
TQByteArray packedArgs;
|
|
TQDataStream stream( packedArgs, IO_WriteOnly );
|
|
|
|
stream << ( int ) 'N';
|
|
|
|
TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false );
|
|
TDEIO::Scheduler::assignJobToSlave(mSlave, job);
|
|
connect( job, TQT_SIGNAL(result( TDEIO::Job * ) ),
|
|
this, TQT_SLOT( slotSimpleResult( TDEIO::Job * ) ) );
|
|
} else {
|
|
/* Stop the timer, we have disconnected. We have to make sure it is
|
|
started again when a new slave appears. */
|
|
mNoopTimer.stop();
|
|
}
|
|
}
|
|
|
|
void ImapAccountBase::slotIdleTimeout()
|
|
{
|
|
if ( mSlave ) {
|
|
TDEIO::Scheduler::disconnectSlave(mSlave);
|
|
mSlave = 0;
|
|
mSlaveConnected = false;
|
|
/* As for the noop timer, we need to make sure this one is started
|
|
again when a new slave goes up. */
|
|
mIdleTimer.stop();
|
|
}
|
|
}
|
|
|
|
void ImapAccountBase::slotAbortRequested( KPIM::ProgressItem* item )
|
|
{
|
|
if ( item )
|
|
item->setComplete();
|
|
killAllJobs();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotSchedulerSlaveError(TDEIO::Slave *aSlave, int errorCode,
|
|
const TQString &errorMsg)
|
|
{
|
|
if (aSlave != mSlave) return;
|
|
handleError( errorCode, errorMsg, 0, TQString(), true );
|
|
if ( mAskAgain )
|
|
if ( makeConnection() != ImapAccountBase::Error )
|
|
return;
|
|
|
|
if ( !mSlaveConnected ) {
|
|
mSlaveConnectionError = true;
|
|
resetConnectionList( this );
|
|
if ( mSlave )
|
|
{
|
|
TDEIO::Scheduler::disconnectSlave( slave() );
|
|
mSlave = 0;
|
|
}
|
|
}
|
|
emit connectionResult( errorCode, errorMsg );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotSchedulerSlaveConnected(TDEIO::Slave *aSlave)
|
|
{
|
|
if (aSlave != mSlave) return;
|
|
mSlaveConnected = true;
|
|
mNoopTimer.start( 60000 ); // make sure we start sending noops
|
|
emit connectionResult( 0, TQString() ); // success
|
|
|
|
if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
|
|
connect( this, TQT_SIGNAL( namespacesFetched( const ImapAccountBase::nsDelimMap& ) ),
|
|
this, TQT_SLOT( slotSaveNamespaces( const ImapAccountBase::nsDelimMap& ) ) );
|
|
getNamespaces();
|
|
}
|
|
|
|
// get capabilities
|
|
TQByteArray packedArgs;
|
|
TQDataStream stream( packedArgs, IO_WriteOnly);
|
|
stream << (int) 'c';
|
|
TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false );
|
|
TDEIO::Scheduler::assignJobToSlave( mSlave, job );
|
|
connect( job, TQT_SIGNAL(infoMessage(TDEIO::Job*, const TQString&)),
|
|
TQT_SLOT(slotCapabilitiesResult(TDEIO::Job*, const TQString&)) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotCapabilitiesResult( TDEIO::Job*, const TQString& result )
|
|
{
|
|
mCapabilities = TQStringList::split(' ', result.lower() );
|
|
kdDebug(5006) << "capabilities:" << mCapabilities << endl;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::getNamespaces()
|
|
{
|
|
disconnect( this, TQT_SIGNAL( connectionResult(int, const TQString&) ),
|
|
this, TQT_SLOT( getNamespaces() ) );
|
|
if ( makeConnection() != Connected || !mSlave ) {
|
|
kdDebug(5006) << "getNamespaces - wait for connection" << endl;
|
|
if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
|
|
// when the connection is established slotSchedulerSlaveConnected notifies us
|
|
} else {
|
|
// getNamespaces was called by someone else
|
|
connect( this, TQT_SIGNAL( connectionResult(int, const TQString&) ),
|
|
this, TQT_SLOT( getNamespaces() ) );
|
|
}
|
|
return;
|
|
}
|
|
|
|
TQByteArray packedArgs;
|
|
TQDataStream stream( packedArgs, IO_WriteOnly);
|
|
stream << (int) 'n';
|
|
jobData jd;
|
|
jd.total = 1; jd.done = 0; jd.cancellable = true;
|
|
jd.progressItem = ProgressManager::createProgressItem(
|
|
ProgressManager::getUniqueID(),
|
|
i18n("Retrieving Namespaces"),
|
|
TQString(), true, useSSL() || useTLS() );
|
|
jd.progressItem->setTotalItems( 1 );
|
|
connect ( jd.progressItem,
|
|
TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
|
|
this,
|
|
TQT_SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
|
|
TDEIO::SimpleJob *job = TDEIO::special( getUrl(), packedArgs, false );
|
|
TDEIO::Scheduler::assignJobToSlave( mSlave, job );
|
|
insertJob( job, jd );
|
|
connect( job, TQT_SIGNAL( infoMessage(TDEIO::Job*, const TQString&) ),
|
|
TQT_SLOT( slotNamespaceResult(TDEIO::Job*, const TQString&) ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotNamespaceResult( TDEIO::Job* job, const TQString& str )
|
|
{
|
|
JobIterator it = findJob( job );
|
|
if ( it == jobsEnd() ) return;
|
|
|
|
nsDelimMap map;
|
|
namespaceDelim nsDelim;
|
|
TQStringList ns = TQStringList::split( ",", str );
|
|
for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it ) {
|
|
// split, allow empty parts as we can get empty namespaces
|
|
TQStringList parts = TQStringList::split( "=", *it, true );
|
|
imapNamespace section = imapNamespace( parts[0].toInt() );
|
|
if ( map.contains( section ) ) {
|
|
nsDelim = map[section];
|
|
} else {
|
|
nsDelim.clear();
|
|
}
|
|
// map namespace to delimiter
|
|
nsDelim[parts[1]] = parts[2];
|
|
map[section] = nsDelim;
|
|
}
|
|
removeJob(it);
|
|
|
|
kdDebug(5006) << "namespaces fetched" << endl;
|
|
emit namespacesFetched( map );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotSaveNamespaces( const ImapAccountBase::nsDelimMap& map )
|
|
{
|
|
kdDebug(5006) << "slotSaveNamespaces " << name() << endl;
|
|
// extract the needed information
|
|
mNamespaces.clear();
|
|
mNamespaceToDelimiter.clear();
|
|
for ( uint i = 0; i < 3; ++i ) {
|
|
imapNamespace section = imapNamespace( i );
|
|
namespaceDelim ns = map[ section ];
|
|
namespaceDelim::ConstIterator it;
|
|
TQStringList list;
|
|
for ( it = ns.begin(); it != ns.end(); ++it ) {
|
|
list += it.key();
|
|
mNamespaceToDelimiter[ it.key() ] = it.data();
|
|
}
|
|
if ( !list.isEmpty() ) {
|
|
mNamespaces[section] = list;
|
|
}
|
|
}
|
|
// see if we need to migrate an old prefix
|
|
if ( !mOldPrefix.isEmpty() ) {
|
|
migratePrefix();
|
|
}
|
|
emit namespacesFetched();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::migratePrefix()
|
|
{
|
|
if ( !mOldPrefix.isEmpty() && mOldPrefix != "/" ) {
|
|
// strip /
|
|
if ( mOldPrefix.startsWith("/") ) {
|
|
mOldPrefix = mOldPrefix.right( mOldPrefix.length()-1 );
|
|
}
|
|
if ( mOldPrefix.endsWith("/") ) {
|
|
mOldPrefix = mOldPrefix.left( mOldPrefix.length()-1 );
|
|
}
|
|
TQStringList list = mNamespaces[PersonalNS];
|
|
bool done = false;
|
|
for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
|
|
if ( (*it).startsWith( mOldPrefix ) ) {
|
|
// should be ok
|
|
done = true;
|
|
kdDebug(5006) << "migratePrefix - no migration needed" << endl;
|
|
break;
|
|
}
|
|
}
|
|
if ( !done ) {
|
|
TQString msg = i18n("KMail has detected a prefix entry in the "
|
|
"configuration of the account \"%1\" which is obsolete with the "
|
|
"support of IMAP namespaces.").arg( name() );
|
|
if ( list.contains( "" ) ) {
|
|
// replace empty entry with the old prefix
|
|
list.remove( "" );
|
|
list += mOldPrefix;
|
|
mNamespaces[PersonalNS] = list;
|
|
if ( mNamespaceToDelimiter.contains( "" ) ) {
|
|
TQString delim = mNamespaceToDelimiter[""];
|
|
mNamespaceToDelimiter.remove( "" );
|
|
mNamespaceToDelimiter[mOldPrefix] = delim;
|
|
}
|
|
kdDebug(5006) << "migratePrefix - replaced empty with " << mOldPrefix << endl;
|
|
msg += i18n("The configuration was automatically migrated but you should check "
|
|
"your account configuration.");
|
|
} else if ( list.count() == 1 ) {
|
|
// only one entry in the personal namespace so replace it
|
|
TQString old = list.first();
|
|
list.clear();
|
|
list += mOldPrefix;
|
|
mNamespaces[PersonalNS] = list;
|
|
if ( mNamespaceToDelimiter.contains( old ) ) {
|
|
TQString delim = mNamespaceToDelimiter[old];
|
|
mNamespaceToDelimiter.remove( old );
|
|
mNamespaceToDelimiter[mOldPrefix] = delim;
|
|
}
|
|
kdDebug(5006) << "migratePrefix - replaced single with " << mOldPrefix << endl;
|
|
msg += i18n("The configuration was automatically migrated but you should check "
|
|
"your account configuration.");
|
|
} else {
|
|
kdDebug(5006) << "migratePrefix - migration failed" << endl;
|
|
msg += i18n("It was not possible to migrate your configuration automatically "
|
|
"so please check your account configuration.");
|
|
}
|
|
KMessageBox::information( kmkernel->getKMMainWidget(), msg );
|
|
}
|
|
} else
|
|
{
|
|
kdDebug(5006) << "migratePrefix - no migration needed" << endl;
|
|
}
|
|
mOldPrefix = "";
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
TQString ImapAccountBase::namespaceForFolder( FolderStorage* storage )
|
|
{
|
|
TQString path;
|
|
if ( storage->folderType() == KMFolderTypeImap ) {
|
|
path = static_cast<KMFolderImap*>( storage )->imapPath();
|
|
} else if ( storage->folderType() == KMFolderTypeCachedImap ) {
|
|
path = static_cast<KMFolderCachedImap*>( storage )->imapPath();
|
|
}
|
|
|
|
nsMap::Iterator it;
|
|
for ( it = mNamespaces.begin(); it != mNamespaces.end(); ++it )
|
|
{
|
|
TQStringList::Iterator strit;
|
|
for ( strit = it.data().begin(); strit != it.data().end(); ++strit )
|
|
{
|
|
TQString ns = *strit;
|
|
if ( ns.endsWith("/") || ns.endsWith(".") ) {
|
|
// strip delimiter for the comparison
|
|
ns = ns.left( ns.length()-1 );
|
|
}
|
|
// first ignore an empty prefix as it would match always
|
|
if ( !ns.isEmpty() && path.find( ns ) != -1 ) {
|
|
return (*strit);
|
|
}
|
|
}
|
|
}
|
|
return TQString();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
TQString ImapAccountBase::delimiterForNamespace( const TQString& prefix )
|
|
{
|
|
//kdDebug(5006) << "delimiterForNamespace " << prefix << endl;
|
|
// try to match exactly
|
|
if ( mNamespaceToDelimiter.contains(prefix) ) {
|
|
return mNamespaceToDelimiter[prefix];
|
|
}
|
|
|
|
// then try if the prefix is part of a namespace
|
|
// exclude empty namespace
|
|
for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin();
|
|
it != mNamespaceToDelimiter.end(); ++it ) {
|
|
// the namespace definition sometimes contains the delimiter
|
|
// make sure we also match this version
|
|
TQString stripped = it.key().left( it.key().length() - 1 );
|
|
if ( !it.key().isEmpty() &&
|
|
( prefix.contains( it.key() ) || prefix.contains( stripped ) ) ) {
|
|
return it.data();
|
|
}
|
|
}
|
|
// see if we have an empty namespace
|
|
// this should always be the case
|
|
if ( mNamespaceToDelimiter.contains( "" ) ) {
|
|
return mNamespaceToDelimiter[""];
|
|
}
|
|
// well, we tried
|
|
//kdDebug(5006) << "delimiterForNamespace - not found" << endl;
|
|
return TQString();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
TQString ImapAccountBase::delimiterForFolder( FolderStorage* storage )
|
|
{
|
|
TQString prefix = namespaceForFolder( storage );
|
|
TQString delim = delimiterForNamespace( prefix );
|
|
return delim;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotSimpleResult(TDEIO::Job * job)
|
|
{
|
|
JobIterator it = findJob( job );
|
|
bool quiet = false;
|
|
if (it != mapJobData.end()) {
|
|
quiet = (*it).quiet;
|
|
if ( !(job->error() && !quiet) ) // the error handler removes in that case
|
|
removeJob(it);
|
|
}
|
|
if (job->error()) {
|
|
if (!quiet)
|
|
handleJobError(job, TQString() );
|
|
else {
|
|
if ( job->error() == TDEIO::ERR_CONNECTION_BROKEN && slave() ) {
|
|
// make sure ERR_CONNECTION_BROKEN is properly handled and the slave
|
|
// disconnected even when quiet()
|
|
TDEIO::Scheduler::disconnectSlave( slave() );
|
|
mSlave = 0;
|
|
}
|
|
if (job->error() == TDEIO::ERR_SLAVE_DIED)
|
|
slaveDied();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool ImapAccountBase::handlePutError( TDEIO::Job* job, jobData& jd, KMFolder* folder )
|
|
{
|
|
Q_ASSERT( !jd.msgList.isEmpty() );
|
|
KMMessage* msg = jd.msgList.first();
|
|
// Use double-quotes around the subject to keep the sentence readable,
|
|
// but don't use double quotes around the sender since from() might return a double-quoted name already
|
|
const TQString subject = msg->subject().isEmpty() ? i18n( "<unknown>" ) : TQString("\"%1\"").arg( msg->subject() );
|
|
const TQString from = msg->from().isEmpty() ? i18n( "<unknown>" ) : msg->from();
|
|
TQString myError = "<p><b>" + i18n("Error while uploading message")
|
|
+ "</b></p><p>"
|
|
+ i18n("Could not upload the message dated %1 from <i>%2</i> with subject <i>%3</i> to the server.").arg( msg->dateStr(), TQStyleSheet::escape( from ), TQStyleSheet::escape( subject ) )
|
|
+ "</p><p>"
|
|
+ i18n("The destination folder was: <b>%1</b>.").arg( TQStyleSheet::escape( folder->prettyURL() ) )
|
|
+ "</p><p>"
|
|
+ i18n("The server reported:") + "</p>";
|
|
return handleJobError( job, myError );
|
|
}
|
|
|
|
TQString ImapAccountBase::prettifyQuotaError( const TQString& _error, TDEIO::Job * job )
|
|
{
|
|
TQString error = _error;
|
|
if ( error.find( "quota", 0, false ) == -1 ) return error;
|
|
// this is a quota error, prettify it a bit
|
|
JobIterator it = findJob( job );
|
|
TQString quotaAsString( i18n("No detailed quota information available.") );
|
|
bool readOnly = false;
|
|
if (it != mapJobData.end()) {
|
|
const KMFolder * const folder = (*it).parent;
|
|
if( !folder ) return _error;
|
|
const KMFolderCachedImap * const imap = dynamic_cast<const KMFolderCachedImap*>( folder->storage() );
|
|
if ( imap ) {
|
|
quotaAsString = imap->quotaInfo().toString();
|
|
}
|
|
readOnly = folder->isReadOnly();
|
|
}
|
|
error = i18n("The folder is too close to its quota limit. (%1)").arg( quotaAsString );
|
|
if ( readOnly ) {
|
|
error += i18n("\nSince you do not have write privileges on this folder, "
|
|
"please ask the owner of the folder to free up some space in it.");
|
|
}
|
|
return error;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool ImapAccountBase::handleError( int errorCode, const TQString &errorMsg, TDEIO::Job* job, const TQString& context, bool abortSync )
|
|
{
|
|
// Copy job's data before a possible killAllJobs
|
|
TQStringList errors;
|
|
if ( job && job->error() != TDEIO::ERR_SLAVE_DEFINED /*workaround for tdelibs-3.2*/)
|
|
errors = job->detailedErrorStrings();
|
|
|
|
bool jobsKilled = true;
|
|
switch( errorCode ) {
|
|
case TDEIO::ERR_SLAVE_DIED: slaveDied(); killAllJobs( true ); break;
|
|
case TDEIO::ERR_COULD_NOT_AUTHENTICATE: // bad password
|
|
mAskAgain = true;
|
|
// fallthrough intended
|
|
case TDEIO::ERR_CONNECTION_BROKEN:
|
|
case TDEIO::ERR_COULD_NOT_CONNECT:
|
|
case TDEIO::ERR_SERVER_TIMEOUT:
|
|
// These mean that we'll have to reconnect on the next attempt, so disconnect and set mSlave to 0.
|
|
killAllJobs( true );
|
|
break;
|
|
case TDEIO::ERR_COULD_NOT_LOGIN:
|
|
case TDEIO::ERR_USER_CANCELED:
|
|
killAllJobs( false );
|
|
break;
|
|
default:
|
|
if ( abortSync )
|
|
killAllJobs( false );
|
|
else
|
|
jobsKilled = false;
|
|
break;
|
|
}
|
|
|
|
// check if we still display an error
|
|
if ( !mErrorDialogIsActive && errorCode != TDEIO::ERR_USER_CANCELED ) {
|
|
mErrorDialogIsActive = true;
|
|
TQString msg = context + '\n' + prettifyQuotaError( TDEIO::buildErrorString( errorCode, errorMsg ), job );
|
|
TQString caption = i18n("Error");
|
|
|
|
if ( jobsKilled || errorCode == TDEIO::ERR_COULD_NOT_LOGIN ) {
|
|
if ( errorCode == TDEIO::ERR_SERVER_TIMEOUT || errorCode == TDEIO::ERR_CONNECTION_BROKEN ) {
|
|
msg = i18n("The connection to the server %1 was unexpectedly closed or timed out. It will be re-established automatically if possible.").
|
|
arg( name() );
|
|
KMessageBox::information( TQT_TQWIDGET(kapp->activeWindow()), msg, caption, "kmailConnectionBrokenErrorDialog" );
|
|
// Show it in the status bar, in case the user has ticked "don't show again"
|
|
if ( errorCode == TDEIO::ERR_CONNECTION_BROKEN )
|
|
KPIM::BroadcastStatus::instance()->setStatusMsg(
|
|
i18n( "The connection to account %1 was broken." ).arg( name() ) );
|
|
else if ( errorCode == TDEIO::ERR_SERVER_TIMEOUT )
|
|
KPIM::BroadcastStatus::instance()->setStatusMsg(
|
|
i18n( "The connection to account %1 timed out." ).arg( name() ) );
|
|
} else {
|
|
if ( !errors.isEmpty() )
|
|
KMessageBox::detailedError( TQT_TQWIDGET(kapp->activeWindow()), msg, errors.join("\n").prepend("<qt>"), caption );
|
|
else
|
|
KMessageBox::error( TQT_TQWIDGET(kapp->activeWindow()), msg, caption );
|
|
}
|
|
} else { // i.e. we have a chance to continue, ask the user about it
|
|
if ( errors.count() >= 3 ) { // there is no detailedWarningContinueCancel... (#86517)
|
|
TQString error = prettifyQuotaError( errors[1], job );
|
|
msg = TQString( "<qt>") + context + error + '\n' + errors[2];
|
|
caption = errors[0];
|
|
}
|
|
int ret = KMessageBox::warningContinueCancel( TQT_TQWIDGET(kapp->activeWindow()), msg, caption );
|
|
if ( ret == KMessageBox::Cancel ) {
|
|
jobsKilled = true;
|
|
killAllJobs( false );
|
|
}
|
|
}
|
|
mErrorDialogIsActive = false;
|
|
} else {
|
|
if ( mErrorDialogIsActive )
|
|
kdDebug(5006) << "suppressing error:" << errorMsg << endl;
|
|
}
|
|
if ( job && !jobsKilled )
|
|
removeJob( job );
|
|
return !jobsKilled; // jobsKilled==false -> continue==true
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::cancelMailCheck()
|
|
{
|
|
TQMap<TDEIO::Job*, jobData>::Iterator it = mapJobData.begin();
|
|
while ( it != mapJobData.end() ) {
|
|
kdDebug(5006) << "cancelMailCheck: job is cancellable: " << (*it).cancellable << endl;
|
|
if ( (*it).cancellable ) {
|
|
it.key()->kill();
|
|
TQMap<TDEIO::Job*, jobData>::Iterator rmit = it;
|
|
++it;
|
|
mapJobData.remove( rmit );
|
|
// We killed a job -> this kills the slave
|
|
mSlave = 0;
|
|
} else
|
|
++it;
|
|
}
|
|
|
|
for( TQPtrListIterator<FolderJob> it( mJobList ); it.current(); ++it ) {
|
|
if ( it.current()->isCancellable() ) {
|
|
FolderJob* job = it.current();
|
|
job->setPassiveDestructor( true );
|
|
mJobList.remove( job );
|
|
delete job;
|
|
} else
|
|
++it;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::processNewMailInFolder( KMFolder* folder, FolderListType type /*= Single*/ )
|
|
{
|
|
if ( mFoldersQueuedForChecking.contains( folder ) )
|
|
return;
|
|
mFoldersQueuedForChecking.append( folder );
|
|
mCheckingSingleFolder = ( type == Single );
|
|
if ( checkingMail() )
|
|
{
|
|
disconnect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ),
|
|
this, TQT_SLOT( slotCheckQueuedFolders() ) );
|
|
connect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ),
|
|
this, TQT_SLOT( slotCheckQueuedFolders() ) );
|
|
} else {
|
|
slotCheckQueuedFolders();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotCheckQueuedFolders()
|
|
{
|
|
disconnect( this, TQT_SIGNAL( finishedCheck( bool, CheckStatus ) ),
|
|
this, TQT_SLOT( slotCheckQueuedFolders() ) );
|
|
|
|
TQValueList<TQGuardedPtr<KMFolder> > mSaveList = mMailCheckFolders;
|
|
mMailCheckFolders = mFoldersQueuedForChecking;
|
|
if ( kmkernel->acctMgr() )
|
|
kmkernel->acctMgr()->singleCheckMail(this, true);
|
|
mMailCheckFolders = mSaveList;
|
|
mFoldersQueuedForChecking.clear();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool ImapAccountBase::checkingMail( KMFolder *folder )
|
|
{
|
|
if (checkingMail() && mFoldersQueuedForChecking.contains(folder))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::handleBodyStructure( TQDataStream & stream, KMMessage * msg,
|
|
const AttachmentStrategy *as )
|
|
{
|
|
mBodyPartList.clear();
|
|
mCurrentMsg = msg;
|
|
// first delete old parts as we construct our own
|
|
msg->deleteBodyParts();
|
|
// make the parts and fill the mBodyPartList
|
|
constructParts( stream, 1, 0, 0, msg->asDwMessage() );
|
|
if ( mBodyPartList.count() == 1 ) // we directly set the body later, at partsToLoad below
|
|
msg->deleteBodyParts();
|
|
|
|
if ( !as )
|
|
{
|
|
kdWarning(5006) << k_funcinfo << " - found no attachment strategy!" << endl;
|
|
return;
|
|
}
|
|
|
|
// see what parts have to loaded according to attachmentstrategy
|
|
BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as );
|
|
visitor->visit( mBodyPartList );
|
|
TQPtrList<KMMessagePart> parts = visitor->partsToLoad();
|
|
delete visitor;
|
|
TQPtrListIterator<KMMessagePart> it( parts );
|
|
KMMessagePart *part;
|
|
int partsToLoad = 0;
|
|
// check how many parts we have to load
|
|
while ( (part = it.current()) != 0 )
|
|
{
|
|
++it;
|
|
if ( part->loadPart() )
|
|
{
|
|
++partsToLoad;
|
|
}
|
|
}
|
|
// if the only body part is not text, part->loadPart() would return false
|
|
// and that part is never loaded, so make sure it loads.
|
|
// it seems that TEXT does load the single body part even if it is not text/*
|
|
if ( mBodyPartList.count() == 1 && partsToLoad == 0 )
|
|
partsToLoad = 1; // this causes the next test to succeed, and loads the whole message
|
|
|
|
if ( (mBodyPartList.count() * 0.5) < partsToLoad )
|
|
{
|
|
// more than 50% of the parts have to be loaded anyway so it is faster
|
|
// to load the message completely
|
|
kdDebug(5006) << "Falling back to normal mode" << endl;
|
|
FolderJob *job = msg->parent()->createJob(
|
|
msg, FolderJob::tGetMessage, 0, "TEXT" );
|
|
job->start();
|
|
return;
|
|
}
|
|
it.toFirst();
|
|
while ( (part = it.current()) != 0 )
|
|
{
|
|
++it;
|
|
kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier()
|
|
<< " (" << part->originalContentTypeStr() << ")" << endl;
|
|
if ( part->loadHeaders() )
|
|
{
|
|
kdDebug(5006) << "load HEADER" << endl;
|
|
FolderJob *job = msg->parent()->createJob(
|
|
msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" );
|
|
job->start();
|
|
}
|
|
if ( part->loadPart() )
|
|
{
|
|
kdDebug(5006) << "load Part" << endl;
|
|
FolderJob *job = msg->parent()->createJob(
|
|
msg, FolderJob::tGetMessage, 0, part->partSpecifier() );
|
|
job->start();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::constructParts( TQDataStream & stream, int count, KMMessagePart* parentKMPart,
|
|
DwBodyPart * parent, const DwMessage * dwmsg )
|
|
{
|
|
int children;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
stream >> children;
|
|
KMMessagePart* part = new KMMessagePart( stream );
|
|
part->setParent( parentKMPart );
|
|
mBodyPartList.append( part );
|
|
kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier()
|
|
<< " of type " << part->originalContentTypeStr() << endl;
|
|
DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part );
|
|
|
|
if ( parent )
|
|
{
|
|
// add to parent body
|
|
parent->Body().AddBodyPart( dwpart );
|
|
dwpart->Parse();
|
|
// kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
|
|
// << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
|
|
} else if ( part->partSpecifier() != "0" &&
|
|
!part->partSpecifier().endsWith(".HEADER") )
|
|
{
|
|
// add to message
|
|
dwmsg->Body().AddBodyPart( dwpart );
|
|
dwpart->Parse();
|
|
// kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
|
|
// << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
|
|
} else
|
|
dwpart = 0;
|
|
|
|
if ( !parentKMPart )
|
|
parentKMPart = part;
|
|
|
|
if (children > 0)
|
|
{
|
|
DwBodyPart* newparent = dwpart;
|
|
const DwMessage* newmsg = dwmsg;
|
|
if ( part->originalContentTypeStr() == "MESSAGE/RFC822" && dwpart &&
|
|
dwpart->Body().Message() )
|
|
{
|
|
// set the encapsulated message as the new message
|
|
newparent = 0;
|
|
newmsg = dwpart->Body().Message();
|
|
}
|
|
KMMessagePart* newParentKMPart = part;
|
|
if ( part->partSpecifier().endsWith(".HEADER") ) // we don't want headers as parent
|
|
newParentKMPart = parentKMPart;
|
|
|
|
constructParts( stream, children, newParentKMPart, newparent, newmsg );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::setImapStatus( KMFolder* folder, const TQString& path, const TQCString& flags )
|
|
{
|
|
// set the status on the server, the uids are integrated in the path
|
|
kdDebug(5006) << "setImapStatus path=" << path << " to: " << flags << endl;
|
|
KURL url = getUrl();
|
|
url.setPath(path);
|
|
|
|
TQByteArray packedArgs;
|
|
TQDataStream stream( packedArgs, IO_WriteOnly);
|
|
|
|
stream << (int) 'S' << url << flags;
|
|
|
|
if ( makeConnection() != Connected )
|
|
return; // can't happen with dimap
|
|
|
|
TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false);
|
|
TDEIO::Scheduler::assignJobToSlave(slave(), job);
|
|
ImapAccountBase::jobData jd( url.url(), folder );
|
|
jd.path = path;
|
|
insertJob(job, jd);
|
|
connect(job, TQT_SIGNAL(result(TDEIO::Job *)),
|
|
TQT_SLOT(slotSetStatusResult(TDEIO::Job *)));
|
|
}
|
|
|
|
void ImapAccountBase::setImapSeenStatus(KMFolder * folder, const TQString & path, bool seen)
|
|
{
|
|
KURL url = getUrl();
|
|
url.setPath(path);
|
|
|
|
TQByteArray packedArgs;
|
|
TQDataStream stream( packedArgs, IO_WriteOnly);
|
|
|
|
stream << (int) 's' << url << seen;
|
|
|
|
if ( makeConnection() != Connected )
|
|
return; // can't happen with dimap
|
|
|
|
TDEIO::SimpleJob *job = TDEIO::special(url, packedArgs, false);
|
|
TDEIO::Scheduler::assignJobToSlave(slave(), job);
|
|
ImapAccountBase::jobData jd( url.url(), folder );
|
|
jd.path = path;
|
|
insertJob(job, jd);
|
|
connect(job, TQT_SIGNAL(result(TDEIO::Job *)),
|
|
TQT_SLOT(slotSetStatusResult(TDEIO::Job *)));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::slotSetStatusResult(TDEIO::Job * job)
|
|
{
|
|
ImapAccountBase::JobIterator it = findJob(job);
|
|
if ( it == jobsEnd() ) return;
|
|
int errorCode = job->error();
|
|
KMFolder * const parent = (*it).parent;
|
|
const TQString path = (*it).path;
|
|
if (errorCode && errorCode != TDEIO::ERR_CANNOT_OPEN_FOR_WRITING)
|
|
{
|
|
bool cont = handleJobError( job, i18n( "Error while uploading status of messages to server: " ) + '\n' );
|
|
emit imapStatusChanged( parent, path, cont );
|
|
}
|
|
else
|
|
{
|
|
emit imapStatusChanged( parent, path, true );
|
|
removeJob(it);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::setFolder(KMFolder* folder, bool addAccount)
|
|
{
|
|
if (folder)
|
|
{
|
|
folder->setSystemLabel(name());
|
|
folder->setId(id());
|
|
}
|
|
NetworkAccount::setFolder(folder, addAccount);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ImapAccountBase::removeJob( JobIterator& it )
|
|
{
|
|
if( (*it).progressItem ) {
|
|
(*it).progressItem->setComplete();
|
|
(*it).progressItem = 0;
|
|
}
|
|
mapJobData.remove( it );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void KMail::ImapAccountBase::removeJob( TDEIO::Job* job )
|
|
{
|
|
mapJobData.remove( job );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
KPIM::ProgressItem* ImapAccountBase::listDirProgressItem()
|
|
{
|
|
if ( !mListDirProgressItem )
|
|
{
|
|
mListDirProgressItem = ProgressManager::createProgressItem(
|
|
"ListDir" + name(),
|
|
TQStyleSheet::escape( name() ),
|
|
i18n("retrieving folders"),
|
|
true,
|
|
useSSL() || useTLS() );
|
|
connect ( mListDirProgressItem,
|
|
TQT_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
|
|
this,
|
|
TQT_SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
|
|
// Start with a guessed value of the old folder count plus 5%. As long
|
|
// as the list of folders doesn't constantly change, that should be good
|
|
// enough.
|
|
unsigned int count = folderCount();
|
|
mListDirProgressItem->setTotalItems( count + (unsigned int)(count*0.05) );
|
|
}
|
|
return mListDirProgressItem;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
unsigned int ImapAccountBase::folderCount() const
|
|
{
|
|
if ( !rootFolder() || !rootFolder()->folder() || !rootFolder()->folder()->child() )
|
|
return 0;
|
|
return kmkernel->imapFolderMgr()->folderCount( rootFolder()->folder()->child() );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
TQString ImapAccountBase::addPathToNamespace( const TQString& prefix )
|
|
{
|
|
TQString myPrefix = prefix;
|
|
if ( !myPrefix.startsWith( "/" ) ) {
|
|
myPrefix = "/" + myPrefix;
|
|
}
|
|
if ( !myPrefix.endsWith( "/" ) ) {
|
|
myPrefix += "/";
|
|
}
|
|
|
|
return myPrefix;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool ImapAccountBase::isNamespaceFolder( TQString& name )
|
|
{
|
|
TQStringList ns = mNamespaces[OtherUsersNS];
|
|
ns += mNamespaces[SharedNS];
|
|
ns += mNamespaces[PersonalNS];
|
|
TQString nameWithDelimiter;
|
|
for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
|
|
{
|
|
nameWithDelimiter = name + delimiterForNamespace( *it );
|
|
if ( *it == name || *it == nameWithDelimiter )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
ImapAccountBase::nsDelimMap ImapAccountBase::namespacesWithDelimiter()
|
|
{
|
|
nsDelimMap map;
|
|
nsMap::ConstIterator it;
|
|
for ( uint i = 0; i < 3; ++i )
|
|
{
|
|
imapNamespace section = imapNamespace( i );
|
|
TQStringList namespaces = mNamespaces[section];
|
|
namespaceDelim nsDelim;
|
|
TQStringList::Iterator lit;
|
|
for ( lit = namespaces.begin(); lit != namespaces.end(); ++lit )
|
|
{
|
|
nsDelim[*lit] = delimiterForNamespace( *lit );
|
|
}
|
|
map[section] = nsDelim;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
TQString ImapAccountBase::createImapPath( const TQString& parent,
|
|
const TQString& folderName )
|
|
{
|
|
kdDebug(5006) << "createImapPath parent="<<parent<<", folderName="<<folderName<<endl;
|
|
TQString newName = parent;
|
|
// strip / at the end
|
|
if ( newName.endsWith("/") ) {
|
|
newName = newName.left( newName.length() - 1 );
|
|
}
|
|
// add correct delimiter
|
|
TQString delim = delimiterForNamespace( newName );
|
|
// should not happen...
|
|
if ( delim.isEmpty() ) {
|
|
delim = "/";
|
|
}
|
|
if ( !newName.isEmpty() &&
|
|
!newName.endsWith( delim ) && !folderName.startsWith( delim ) ) {
|
|
newName = newName + delim;
|
|
}
|
|
newName = newName + folderName;
|
|
// add / at the end
|
|
if ( !newName.endsWith("/") ) {
|
|
newName = newName + "/";
|
|
}
|
|
|
|
return newName;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
TQString ImapAccountBase::createImapPath( FolderStorage* parent,
|
|
const TQString& folderName )
|
|
{
|
|
TQString path;
|
|
if ( parent->folderType() == KMFolderTypeImap ) {
|
|
path = static_cast<KMFolderImap*>( parent )->imapPath();
|
|
} else if ( parent->folderType() == KMFolderTypeCachedImap ) {
|
|
path = static_cast<KMFolderCachedImap*>( parent )->imapPath();
|
|
} else {
|
|
// error
|
|
return path;
|
|
}
|
|
|
|
return createImapPath( path, folderName );
|
|
}
|
|
|
|
|
|
bool ImapAccountBase::locallySubscribedTo( const TQString& imapPath )
|
|
{
|
|
return mLocalSubscriptionBlackList.find( imapPath ) == mLocalSubscriptionBlackList.end();
|
|
}
|
|
|
|
void ImapAccountBase::changeLocalSubscription( const TQString& imapPath, bool subscribe )
|
|
{
|
|
if ( subscribe ) {
|
|
// find in blacklist and remove from it
|
|
mLocalSubscriptionBlackList.erase( imapPath );
|
|
} else {
|
|
// blacklist
|
|
mLocalSubscriptionBlackList.insert( imapPath );
|
|
}
|
|
}
|
|
|
|
|
|
TQStringList ImapAccountBase::locallyBlacklistedFolders() const
|
|
{
|
|
TQStringList list;
|
|
std::set<TQString>::const_iterator it = mLocalSubscriptionBlackList.begin();
|
|
std::set<TQString>::const_iterator end = mLocalSubscriptionBlackList.end();
|
|
for ( ; it != end ; ++it )
|
|
list.append( *it );
|
|
return list;
|
|
}
|
|
|
|
void ImapAccountBase::localBlacklistFromStringList( const TQStringList &list )
|
|
{
|
|
for( TQStringList::ConstIterator it = list.constBegin( ); it != list.constEnd( ); ++it )
|
|
mLocalSubscriptionBlackList.insert( *it );
|
|
}
|
|
|
|
} // namespace KMail
|
|
|
|
#include "imapaccountbase.moc"
|