00001
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027
00028 #include "imapaccountbase.h"
00029 using KMail::SieveConfig;
00030
00031 #include "accountmanager.h"
00032 using KMail::AccountManager;
00033 #include "kmfolder.h"
00034 #include "broadcaststatus.h"
00035 using KPIM::BroadcastStatus;
00036 #include "kmmainwin.h"
00037 #include "kmfolderimap.h"
00038 #include "kmmainwidget.h"
00039 #include "kmmainwin.h"
00040 #include "kmmsgpart.h"
00041 #include "acljobs.h"
00042 #include "kmfoldercachedimap.h"
00043 #include "bodyvisitor.h"
00044 using KMail::BodyVisitor;
00045 #include "imapjob.h"
00046 using KMail::ImapJob;
00047 #include "protocols.h"
00048 #include "progressmanager.h"
00049 using KPIM::ProgressManager;
00050 #include "kmfoldermgr.h"
00051 #include "listjob.h"
00052
00053 #include <kapplication.h>
00054 #include <kdebug.h>
00055 #include <kconfig.h>
00056 #include <klocale.h>
00057 #include <kmessagebox.h>
00058 using KIO::MetaData;
00059 #include <kio/passdlg.h>
00060 using KIO::PasswordDialog;
00061 #include <kio/scheduler.h>
00062 #include <kio/slave.h>
00063 #include <mimelib/bodypart.h>
00064 #include <mimelib/body.h>
00065 #include <mimelib/headers.h>
00066 #include <mimelib/message.h>
00067
00068
00069 #include <qregexp.h>
00070 #include <qstylesheet.h>
00071
00072 namespace KMail {
00073
00074 static const unsigned short int imapDefaultPort = 143;
00075
00076
00077
00078
00079
00080
00081
00082 ImapAccountBase::ImapAccountBase( AccountManager * parent, const QString & name, uint id )
00083 : NetworkAccount( parent, name, id ),
00084 mTotal( 0 ),
00085 mCountUnread( 0 ),
00086 mCountLastUnread( 0 ),
00087 mAutoExpunge( true ),
00088 mHiddenFolders( false ),
00089 mOnlySubscribedFolders( false ),
00090 mLoadOnDemand( true ),
00091 mListOnlyOpenFolders( false ),
00092 mProgressEnabled( false ),
00093 mErrorDialogIsActive( false ),
00094 mPasswordDialogIsActive( false ),
00095 mACLSupport( true ),
00096 mAnnotationSupport( true ),
00097 mSlaveConnected( false ),
00098 mSlaveConnectionError( false ),
00099 mCheckingSingleFolder( false ),
00100 mListDirProgressItem( 0 )
00101 {
00102 mPort = imapDefaultPort;
00103 mBodyPartList.setAutoDelete(true);
00104 KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
00105 this, SLOT(slotSchedulerSlaveError(KIO::Slave *, int, const QString &)));
00106 KIO::Scheduler::connect(SIGNAL(slaveConnected(KIO::Slave *)),
00107 this, SLOT(slotSchedulerSlaveConnected(KIO::Slave *)));
00108 connect(&mNoopTimer, SIGNAL(timeout()), SLOT(slotNoopTimeout()));
00109 connect(&mIdleTimer, SIGNAL(timeout()), SLOT(slotIdleTimeout()));
00110 }
00111
00112 ImapAccountBase::~ImapAccountBase() {
00113 kdWarning( mSlave, 5006 )
00114 << "slave should have been destroyed by subclass!" << endl;
00115 }
00116
00117 void ImapAccountBase::init() {
00118 mAutoExpunge = true;
00119 mHiddenFolders = false;
00120 mOnlySubscribedFolders = false;
00121 mLoadOnDemand = true;
00122 mListOnlyOpenFolders = false;
00123 mProgressEnabled = false;
00124 }
00125
00126 void ImapAccountBase::pseudoAssign( const KMAccount * a ) {
00127 NetworkAccount::pseudoAssign( a );
00128
00129 const ImapAccountBase * i = dynamic_cast<const ImapAccountBase*>( a );
00130 if ( !i ) return;
00131
00132 setAutoExpunge( i->autoExpunge() );
00133 setHiddenFolders( i->hiddenFolders() );
00134 setOnlySubscribedFolders( i->onlySubscribedFolders() );
00135 setLoadOnDemand( i->loadOnDemand() );
00136 setListOnlyOpenFolders( i->listOnlyOpenFolders() );
00137 setNamespaces( i->namespaces() );
00138 setNamespaceToDelimiter( i->namespaceToDelimiter() );
00139 }
00140
00141 unsigned short int ImapAccountBase::defaultPort() const {
00142 return imapDefaultPort;
00143 }
00144
00145 QString ImapAccountBase::protocol() const {
00146 return useSSL() ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL;
00147 }
00148
00149
00150
00151
00152
00153
00154
00155 void ImapAccountBase::setAutoExpunge( bool expunge ) {
00156 mAutoExpunge = expunge;
00157 }
00158
00159 void ImapAccountBase::setHiddenFolders( bool show ) {
00160 mHiddenFolders = show;
00161 }
00162
00163 void ImapAccountBase::setOnlySubscribedFolders( bool show ) {
00164 mOnlySubscribedFolders = show;
00165 }
00166
00167 void ImapAccountBase::setLoadOnDemand( bool load ) {
00168 mLoadOnDemand = load;
00169 }
00170
00171 void ImapAccountBase::setListOnlyOpenFolders( bool only ) {
00172 mListOnlyOpenFolders = only;
00173 }
00174
00175
00176
00177
00178
00179
00180
00181 void ImapAccountBase::readConfig( KConfig & config ) {
00182 NetworkAccount::readConfig( config );
00183
00184 setAutoExpunge( config.readBoolEntry( "auto-expunge", false ) );
00185 setHiddenFolders( config.readBoolEntry( "hidden-folders", false ) );
00186 setOnlySubscribedFolders( config.readBoolEntry( "subscribed-folders", false ) );
00187 setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) );
00188 setListOnlyOpenFolders( config.readBoolEntry( "listOnlyOpenFolders", false ) );
00189
00190 nsMap map;
00191 QStringList list = config.readListEntry( QString::number( PersonalNS ) );
00192 if ( !list.isEmpty() )
00193 map[PersonalNS] = list.gres( "\"", "" );
00194 list = config.readListEntry( QString::number( OtherUsersNS ) );
00195 if ( !list.isEmpty() )
00196 map[OtherUsersNS] = list.gres( "\"", "" );
00197 list = config.readListEntry( QString::number( SharedNS ) );
00198 if ( !list.isEmpty() )
00199 map[SharedNS] = list.gres( "\"", "" );
00200 setNamespaces( map );
00201
00202 namespaceDelim entries = config.entryMap( config.group() );
00203 namespaceDelim namespaceToDelimiter;
00204 for ( namespaceDelim::ConstIterator it = entries.begin();
00205 it != entries.end(); ++it ) {
00206 if ( it.key().startsWith( "Namespace:" ) ) {
00207 QString key = it.key().right( it.key().length() - 10 );
00208 namespaceToDelimiter[key] = it.data();
00209 }
00210 }
00211 setNamespaceToDelimiter( namespaceToDelimiter );
00212 mOldPrefix = config.readEntry( "prefix" );
00213 if ( !mOldPrefix.isEmpty() ) {
00214 makeConnection();
00215 }
00216 }
00217
00218 void ImapAccountBase::writeConfig( KConfig & config ) {
00219 NetworkAccount::writeConfig( config );
00220
00221 config.writeEntry( "auto-expunge", autoExpunge() );
00222 config.writeEntry( "hidden-folders", hiddenFolders() );
00223 config.writeEntry( "subscribed-folders", onlySubscribedFolders() );
00224 config.writeEntry( "loadondemand", loadOnDemand() );
00225 config.writeEntry( "listOnlyOpenFolders", listOnlyOpenFolders() );
00226 QString data;
00227 for ( nsMap::Iterator it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) {
00228 if ( !it.data().isEmpty() ) {
00229 data = "\"" + it.data().join("\",\"") + "\"";
00230 config.writeEntry( QString::number( it.key() ), data );
00231 }
00232 }
00233 QString key;
00234 for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin();
00235 it != mNamespaceToDelimiter.end(); ++it ) {
00236 key = "Namespace:" + it.key();
00237 config.writeEntry( key, it.data() );
00238 }
00239 }
00240
00241
00242
00243
00244
00245
00246
00247 MetaData ImapAccountBase::slaveConfig() const {
00248 MetaData m = NetworkAccount::slaveConfig();
00249
00250 m.insert( "auth", auth() );
00251 if ( autoExpunge() )
00252 m.insert( "expunge", "auto" );
00253
00254 return m;
00255 }
00256
00257 ImapAccountBase::ConnectionState ImapAccountBase::makeConnection()
00258 {
00259 if ( mSlave && mSlaveConnected ) {
00260 return Connected;
00261 }
00262 if ( mPasswordDialogIsActive ) return Connecting;
00263
00264 if( mAskAgain || ( ( passwd().isEmpty() || login().isEmpty() ) &&
00265 auth() != "GSSAPI" ) ) {
00266
00267 Q_ASSERT( !mSlave );
00268 QString log = login();
00269 QString pass = passwd();
00270
00271
00272
00273
00274 bool store = true;
00275 KConfigGroup passwords( KGlobal::config(), "Passwords" );
00276 passwords.writeEntry( "Keep", storePasswd() );
00277 QString msg = i18n("You need to supply a username and a password to "
00278 "access this mailbox.");
00279 mPasswordDialogIsActive = true;
00280 if ( PasswordDialog::getNameAndPassword( log, pass, &store, msg, false,
00281 QString::null, name(),
00282 i18n("Account:") )
00283 != QDialog::Accepted ) {
00284 mPasswordDialogIsActive = false;
00285 mAskAgain = false;
00286 emit connectionResult( KIO::ERR_USER_CANCELED, QString::null );
00287 return Error;
00288 }
00289 mPasswordDialogIsActive = false;
00290
00291
00292 setPasswd( pass, store );
00293 setLogin( log );
00294 mAskAgain = false;
00295 }
00296
00297 if ( mSlave && !mSlaveConnected ) return Connecting;
00298
00299 mSlaveConnected = false;
00300 mSlave = KIO::Scheduler::getConnectedSlave( getUrl(), slaveConfig() );
00301 if ( !mSlave ) {
00302 KMessageBox::error(0, i18n("Could not start process for %1.")
00303 .arg( getUrl().protocol() ) );
00304 return Error;
00305 }
00306 if ( mSlave->isConnected() ) {
00307 slotSchedulerSlaveConnected( mSlave );
00308 return Connected;
00309 }
00310
00311 return Connecting;
00312 }
00313
00314 bool ImapAccountBase::handleJobError( KIO::Job *job, const QString& context, bool abortSync )
00315 {
00316 JobIterator it = findJob( job );
00317 if ( it != jobsEnd() && (*it).progressItem )
00318 {
00319 (*it).progressItem->setComplete();
00320 (*it).progressItem = 0;
00321 }
00322 return handleError( job->error(), job->errorText(), job, context, abortSync );
00323 }
00324
00325
00326 void ImapAccountBase::postProcessNewMail( bool showStatusMsg ) {
00327 setCheckingMail(false);
00328 int newMails = 0;
00329 if ( mCountUnread > 0 && mCountUnread > mCountLastUnread ) {
00330 newMails = mCountUnread - mCountLastUnread;
00331 mCountLastUnread = mCountUnread;
00332 mCountUnread = 0;
00333 checkDone( true, CheckOK );
00334 } else {
00335 mCountUnread = 0;
00336 checkDone( false, CheckOK );
00337 }
00338 if ( showStatusMsg )
00339 BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
00340 name(), newMails);
00341 }
00342
00343
00344 void ImapAccountBase::changeSubscription( bool subscribe, const QString& imapPath )
00345 {
00346
00347 KURL url = getUrl();
00348 url.setPath(imapPath);
00349
00350 QByteArray packedArgs;
00351 QDataStream stream( packedArgs, IO_WriteOnly);
00352
00353 if (subscribe)
00354 stream << (int) 'u' << url;
00355 else
00356 stream << (int) 'U' << url;
00357
00358
00359 if ( makeConnection() != Connected )
00360 return;
00361 KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
00362 KIO::Scheduler::assignJobToSlave(mSlave, job);
00363 jobData jd( url.url(), NULL );
00364
00365 if (subscribe) jd.onlySubscribed = true;
00366 else jd.onlySubscribed = false;
00367 insertJob(job, jd);
00368
00369 connect(job, SIGNAL(result(KIO::Job *)),
00370 SLOT(slotSubscriptionResult(KIO::Job *)));
00371 }
00372
00373
00374 void ImapAccountBase::slotSubscriptionResult( KIO::Job * job )
00375 {
00376
00377 JobIterator it = findJob( job );
00378 if ( it == jobsEnd() ) return;
00379 bool onlySubscribed = (*it).onlySubscribed;
00380 QString path = static_cast<KIO::SimpleJob*>(job)->url().path();
00381 if (job->error())
00382 {
00383 handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' );
00384
00385 }
00386 else
00387 {
00388 emit subscriptionChanged( path, onlySubscribed );
00389 if (mSlave) removeJob(job);
00390 }
00391 }
00392
00393
00394
00395 void ImapAccountBase::getUserRights( KMFolder* parent, const QString& imapPath )
00396 {
00397
00398
00399
00400
00401 if ( imapPath == "/INBOX/" ) {
00402 if ( parent->folderType() == KMFolderTypeImap )
00403 static_cast<KMFolderImap*>( parent->storage() )->setUserRights( ACLJobs::All );
00404 else if ( parent->folderType() == KMFolderTypeCachedImap )
00405 static_cast<KMFolderCachedImap*>( parent->storage() )->setUserRights( ACLJobs::All );
00406 emit receivedUserRights( parent );
00407 return;
00408 }
00409
00410 KURL url = getUrl();
00411 url.setPath(imapPath);
00412
00413 ACLJobs::GetUserRightsJob* job = ACLJobs::getUserRights( mSlave, url );
00414
00415 jobData jd( url.url(), parent );
00416 jd.cancellable = true;
00417 insertJob(job, jd);
00418
00419 connect(job, SIGNAL(result(KIO::Job *)),
00420 SLOT(slotGetUserRightsResult(KIO::Job *)));
00421 }
00422
00423 void ImapAccountBase::slotGetUserRightsResult( KIO::Job* _job )
00424 {
00425 ACLJobs::GetUserRightsJob* job = static_cast<ACLJobs::GetUserRightsJob *>( _job );
00426 JobIterator it = findJob( job );
00427 if ( it == jobsEnd() ) return;
00428
00429 KMFolder* folder = (*it).parent;
00430 if ( job->error() ) {
00431 if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION )
00432 mACLSupport = false;
00433 else
00434 kdWarning(5006) << "slotGetUserRightsResult: " << job->errorString() << endl;
00435 } else {
00436 #ifndef NDEBUG
00437
00438 #endif
00439
00440 if ( folder->folderType() == KMFolderTypeImap )
00441 static_cast<KMFolderImap*>( folder->storage() )->setUserRights( job->permissions() );
00442 else if ( folder->folderType() == KMFolderTypeCachedImap )
00443 static_cast<KMFolderCachedImap*>( folder->storage() )->setUserRights( job->permissions() );
00444 }
00445 if (mSlave) removeJob(job);
00446 emit receivedUserRights( folder );
00447 }
00448
00449
00450 void ImapAccountBase::getACL( KMFolder* parent, const QString& imapPath )
00451 {
00452 KURL url = getUrl();
00453 url.setPath(imapPath);
00454
00455 ACLJobs::GetACLJob* job = ACLJobs::getACL( mSlave, url );
00456 jobData jd( url.url(), parent );
00457 jd.cancellable = true;
00458 insertJob(job, jd);
00459
00460 connect(job, SIGNAL(result(KIO::Job *)),
00461 SLOT(slotGetACLResult(KIO::Job *)));
00462 }
00463
00464 void ImapAccountBase::slotGetACLResult( KIO::Job* _job )
00465 {
00466 ACLJobs::GetACLJob* job = static_cast<ACLJobs::GetACLJob *>( _job );
00467 JobIterator it = findJob( job );
00468 if ( it == jobsEnd() ) return;
00469
00470 KMFolder* folder = (*it).parent;
00471 emit receivedACL( folder, job, job->entries() );
00472 if (mSlave) removeJob(job);
00473 }
00474
00475
00476 void ImapAccountBase::slotNoopTimeout()
00477 {
00478 if ( mSlave ) {
00479 QByteArray packedArgs;
00480 QDataStream stream( packedArgs, IO_WriteOnly );
00481
00482 stream << ( int ) 'N';
00483
00484 KIO::SimpleJob *job = KIO::special( getUrl(), packedArgs, false );
00485 KIO::Scheduler::assignJobToSlave(mSlave, job);
00486 connect( job, SIGNAL(result( KIO::Job * ) ),
00487 this, SLOT( slotSimpleResult( KIO::Job * ) ) );
00488 } else {
00489
00490
00491 mNoopTimer.stop();
00492 }
00493 }
00494
00495 void ImapAccountBase::slotIdleTimeout()
00496 {
00497 if ( mSlave ) {
00498 KIO::Scheduler::disconnectSlave(mSlave);
00499 mSlave = 0;
00500 mSlaveConnected = false;
00501
00502
00503 mIdleTimer.stop();
00504 }
00505 }
00506
00507 void ImapAccountBase::slotAbortRequested( KPIM::ProgressItem* item )
00508 {
00509 if ( item )
00510 item->setComplete();
00511 killAllJobs();
00512 }
00513
00514
00515
00516 void ImapAccountBase::slotSchedulerSlaveError(KIO::Slave *aSlave, int errorCode,
00517 const QString &errorMsg)
00518 {
00519 if (aSlave != mSlave) return;
00520 handleError( errorCode, errorMsg, 0, QString::null, true );
00521 if ( mAskAgain )
00522 makeConnection();
00523 else {
00524 if ( !mSlaveConnected ) {
00525 mSlaveConnectionError = true;
00526 resetConnectionList( this );
00527 if ( mSlave )
00528 {
00529 KIO::Scheduler::disconnectSlave( slave() );
00530 mSlave = 0;
00531 }
00532 }
00533 emit connectionResult( errorCode, errorMsg );
00534 }
00535 }
00536
00537
00538 void ImapAccountBase::slotSchedulerSlaveConnected(KIO::Slave *aSlave)
00539 {
00540 if (aSlave != mSlave) return;
00541 mSlaveConnected = true;
00542 mNoopTimer.start( 60000 );
00543 emit connectionResult( 0, QString::null );
00544
00545 if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
00546 connect( this, SIGNAL( namespacesFetched( const ImapAccountBase::nsDelimMap& ) ),
00547 this, SLOT( slotSaveNamespaces( const ImapAccountBase::nsDelimMap& ) ) );
00548 getNamespaces();
00549 }
00550
00551
00552 QByteArray packedArgs;
00553 QDataStream stream( packedArgs, IO_WriteOnly);
00554 stream << (int) 'c';
00555 KIO::SimpleJob *job = KIO::special( getUrl(), packedArgs, false );
00556 KIO::Scheduler::assignJobToSlave( mSlave, job );
00557 connect( job, SIGNAL(infoMessage(KIO::Job*, const QString&)),
00558 SLOT(slotCapabilitiesResult(KIO::Job*, const QString&)) );
00559 }
00560
00561
00562 void ImapAccountBase::slotCapabilitiesResult( KIO::Job*, const QString& result )
00563 {
00564 mCapabilities = QStringList::split(' ', result.lower() );
00565 kdDebug(5006) << "capabilities:" << mCapabilities << endl;
00566 }
00567
00568
00569 void ImapAccountBase::getNamespaces()
00570 {
00571 disconnect( this, SIGNAL( connectionResult(int, const QString&) ),
00572 this, SLOT( getNamespaces() ) );
00573 if ( makeConnection() != Connected || !mSlave ) {
00574 kdDebug(5006) << "getNamespaces - wait for connection" << endl;
00575 if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
00576
00577 } else {
00578
00579 connect( this, SIGNAL( connectionResult(int, const QString&) ),
00580 this, SLOT( getNamespaces() ) );
00581 }
00582 return;
00583 }
00584
00585 QByteArray packedArgs;
00586 QDataStream stream( packedArgs, IO_WriteOnly);
00587 stream << (int) 'n';
00588 jobData jd;
00589 jd.total = 1; jd.done = 0; jd.cancellable = true;
00590 jd.progressItem = ProgressManager::createProgressItem(
00591 ProgressManager::getUniqueID(),
00592 i18n("Retrieving Namespaces"),
00593 QString::null, true, useSSL() || useTLS() );
00594 jd.progressItem->setTotalItems( 1 );
00595 connect ( jd.progressItem,
00596 SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
00597 this,
00598 SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
00599 KIO::SimpleJob *job = KIO::special( getUrl(), packedArgs, false );
00600 KIO::Scheduler::assignJobToSlave( mSlave, job );
00601 insertJob( job, jd );
00602 connect( job, SIGNAL( infoMessage(KIO::Job*, const QString&) ),
00603 SLOT( slotNamespaceResult(KIO::Job*, const QString&) ) );
00604 }
00605
00606
00607 void ImapAccountBase::slotNamespaceResult( KIO::Job* job, const QString& str )
00608 {
00609 JobIterator it = findJob( job );
00610 if ( it == jobsEnd() ) return;
00611
00612 nsDelimMap map;
00613 namespaceDelim nsDelim;
00614 QStringList ns = QStringList::split( ",", str );
00615 for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it ) {
00616
00617 QStringList parts = QStringList::split( "=", *it, true );
00618 imapNamespace section = imapNamespace( parts[0].toInt() );
00619 if ( map.contains( section ) ) {
00620 nsDelim = map[section];
00621 } else {
00622 nsDelim.clear();
00623 }
00624
00625 nsDelim[parts[1]] = parts[2];
00626 map[section] = nsDelim;
00627 }
00628 removeJob(it);
00629
00630 kdDebug(5006) << "namespaces fetched" << endl;
00631 emit namespacesFetched( map );
00632 }
00633
00634
00635 void ImapAccountBase::slotSaveNamespaces( const ImapAccountBase::nsDelimMap& map )
00636 {
00637 kdDebug(5006) << "slotSaveNamespaces " << name() << endl;
00638
00639 mNamespaces.clear();
00640 mNamespaceToDelimiter.clear();
00641 for ( uint i = 0; i < 3; ++i ) {
00642 imapNamespace section = imapNamespace( i );
00643 namespaceDelim ns = map[ section ];
00644 namespaceDelim::ConstIterator it;
00645 QStringList list;
00646 for ( it = ns.begin(); it != ns.end(); ++it ) {
00647 list += it.key();
00648 mNamespaceToDelimiter[ it.key() ] = it.data();
00649 }
00650 if ( !list.isEmpty() ) {
00651 mNamespaces[section] = list;
00652 }
00653 }
00654
00655 if ( !mOldPrefix.isEmpty() ) {
00656 migratePrefix();
00657 }
00658 emit namespacesFetched();
00659 }
00660
00661
00662 void ImapAccountBase::migratePrefix()
00663 {
00664 if ( !mOldPrefix.isEmpty() && mOldPrefix != "/" ) {
00665
00666 if ( mOldPrefix.startsWith("/") ) {
00667 mOldPrefix = mOldPrefix.right( mOldPrefix.length()-1 );
00668 }
00669 if ( mOldPrefix.endsWith("/") ) {
00670 mOldPrefix = mOldPrefix.left( mOldPrefix.length()-1 );
00671 }
00672 QStringList list = mNamespaces[PersonalNS];
00673 bool done = false;
00674 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00675 if ( (*it).startsWith( mOldPrefix ) ) {
00676
00677 done = true;
00678 kdDebug(5006) << "migratePrefix - no migration needed" << endl;
00679 break;
00680 }
00681 }
00682 if ( !done ) {
00683 QString msg = i18n("KMail has detected a prefix entry in the "
00684 "configuration of the account \"%1\" which is obsolete with the "
00685 "support of IMAP namespaces.").arg( name() );
00686 if ( list.contains( "" ) ) {
00687
00688 list.remove( "" );
00689 list += mOldPrefix;
00690 mNamespaces[PersonalNS] = list;
00691 if ( mNamespaceToDelimiter.contains( "" ) ) {
00692 QString delim = mNamespaceToDelimiter[""];
00693 mNamespaceToDelimiter.remove( "" );
00694 mNamespaceToDelimiter[mOldPrefix] = delim;
00695 }
00696 kdDebug(5006) << "migratePrefix - replaced empty with " << mOldPrefix << endl;
00697 msg += i18n("The configuration was automatically migrated but you should check "
00698 "your account configuration.");
00699 } else if ( list.count() == 1 ) {
00700
00701 QString old = list.first();
00702 list.clear();
00703 list += mOldPrefix;
00704 mNamespaces[PersonalNS] = list;
00705 if ( mNamespaceToDelimiter.contains( old ) ) {
00706 QString delim = mNamespaceToDelimiter[old];
00707 mNamespaceToDelimiter.remove( old );
00708 mNamespaceToDelimiter[mOldPrefix] = delim;
00709 }
00710 kdDebug(5006) << "migratePrefix - replaced single with " << mOldPrefix << endl;
00711 msg += i18n("The configuration was automatically migrated but you should check "
00712 "your account configuration.");
00713 } else {
00714 kdDebug(5006) << "migratePrefix - migration failed" << endl;
00715 msg += i18n("It was not possible to migrate your configuration automatically "
00716 "so please check your account configuration.");
00717 }
00718 KMessageBox::information( kmkernel->getKMMainWidget(), msg );
00719 }
00720 } else
00721 {
00722 kdDebug(5006) << "migratePrefix - no migration needed" << endl;
00723 }
00724 mOldPrefix = "";
00725 }
00726
00727
00728 QString ImapAccountBase::namespaceForFolder( FolderStorage* storage )
00729 {
00730 QString path;
00731 if ( storage->folderType() == KMFolderTypeImap ) {
00732 path = static_cast<KMFolderImap*>( storage )->imapPath();
00733 } else if ( storage->folderType() == KMFolderTypeCachedImap ) {
00734 path = static_cast<KMFolderCachedImap*>( storage )->imapPath();
00735 }
00736
00737 nsMap::Iterator it;
00738 for ( it = mNamespaces.begin(); it != mNamespaces.end(); ++it )
00739 {
00740 QStringList::Iterator strit;
00741 for ( strit = it.data().begin(); strit != it.data().end(); ++strit )
00742 {
00743 QString ns = *strit;
00744 if ( ns.endsWith("/") || ns.endsWith(".") ) {
00745
00746 ns = ns.left( ns.length()-1 );
00747 }
00748
00749 if ( !ns.isEmpty() && path.find( ns ) != -1 ) {
00750 return (*strit);
00751 }
00752 }
00753 }
00754 return QString::null;
00755 }
00756
00757
00758 QString ImapAccountBase::delimiterForNamespace( const QString& prefix )
00759 {
00760 kdDebug(5006) << "delimiterForNamespace " << prefix << endl;
00761
00762 if ( mNamespaceToDelimiter.contains(prefix) ) {
00763 return mNamespaceToDelimiter[prefix];
00764 }
00765
00766
00767
00768 for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin();
00769 it != mNamespaceToDelimiter.end(); ++it ) {
00770
00771
00772 QString stripped = it.key().left( it.key().length() - 1 );
00773 if ( !it.key().isEmpty() &&
00774 ( prefix.contains( it.key() ) || prefix.contains( stripped ) ) ) {
00775 return it.data();
00776 }
00777 }
00778
00779
00780 if ( mNamespaceToDelimiter.contains( "" ) ) {
00781 return mNamespaceToDelimiter[""];
00782 }
00783
00784 kdDebug(5006) << "delimiterForNamespace - not found" << endl;
00785 return QString::null;
00786 }
00787
00788
00789 QString ImapAccountBase::delimiterForFolder( FolderStorage* storage )
00790 {
00791 QString prefix = namespaceForFolder( storage );
00792 QString delim = delimiterForNamespace( prefix );
00793 return delim;
00794 }
00795
00796
00797 void ImapAccountBase::slotSimpleResult(KIO::Job * job)
00798 {
00799 JobIterator it = findJob( job );
00800 bool quiet = false;
00801 if (it != mapJobData.end()) {
00802 quiet = (*it).quiet;
00803 if ( !(job->error() && !quiet) )
00804 removeJob(it);
00805 }
00806 if (job->error()) {
00807 if (!quiet)
00808 handleJobError(job, QString::null );
00809 else {
00810 if ( job->error() == KIO::ERR_CONNECTION_BROKEN && slave() ) {
00811
00812
00813 KIO::Scheduler::disconnectSlave( slave() );
00814 mSlave = 0;
00815 }
00816 if (job->error() == KIO::ERR_SLAVE_DIED)
00817 slaveDied();
00818 }
00819 }
00820 }
00821
00822
00823 bool ImapAccountBase::handlePutError( KIO::Job* job, jobData& jd, KMFolder* folder )
00824 {
00825 Q_ASSERT( !jd.msgList.isEmpty() );
00826 KMMessage* msg = jd.msgList.first();
00827
00828
00829 const QString subject = msg->subject().isEmpty() ? i18n( "<unknown>" ) : QString("\"%1\"").arg( msg->subject() );
00830 const QString from = msg->from().isEmpty() ? i18n( "<unknown>" ) : msg->from();
00831 QString myError = "<p><b>" + i18n("Error while uploading message")
00832 + "</b></p><p>"
00833 + i18n("Could not upload the message dated %1 from %2 with subject %3 on the server.").arg( msg->dateStr(), QStyleSheet::escape( from ), QStyleSheet::escape( subject ) )
00834 + "</p><p>"
00835 + i18n("The destination folder was %1, which has the URL %2.").arg( QStyleSheet::escape( folder->label() ), QStyleSheet::escape( jd.htmlURL() ) )
00836 + "</p><p>"
00837 + i18n("The error message from the server communication is here:") + "</p>";
00838 return handleJobError( job, myError );
00839 }
00840
00841
00842 bool ImapAccountBase::handleError( int errorCode, const QString &errorMsg, KIO::Job* job, const QString& context, bool abortSync )
00843 {
00844
00845 QStringList errors;
00846 if ( job && job->error() != KIO::ERR_SLAVE_DEFINED )
00847 errors = job->detailedErrorStrings();
00848
00849 bool jobsKilled = true;
00850 switch( errorCode ) {
00851 case KIO::ERR_SLAVE_DIED: slaveDied(); killAllJobs( true ); break;
00852 case KIO::ERR_COULD_NOT_LOGIN:
00853 mAskAgain = true;
00854
00855 case KIO::ERR_CONNECTION_BROKEN:
00856 case KIO::ERR_COULD_NOT_CONNECT:
00857 case KIO::ERR_SERVER_TIMEOUT:
00858
00859 killAllJobs( true );
00860 break;
00861 case KIO::ERR_USER_CANCELED:
00862 killAllJobs( false );
00863 break;
00864 default:
00865 if ( abortSync )
00866 killAllJobs( false );
00867 else
00868 jobsKilled = false;
00869 break;
00870 }
00871
00872
00873 if ( !mErrorDialogIsActive && errorCode != KIO::ERR_USER_CANCELED ) {
00874 mErrorDialogIsActive = true;
00875 QString msg = context + '\n' + KIO::buildErrorString( errorCode, errorMsg );
00876 QString caption = i18n("Error");
00877
00878 if ( jobsKilled || errorCode == KIO::ERR_COULD_NOT_LOGIN ) {
00879 if ( errorCode == KIO::ERR_SERVER_TIMEOUT || errorCode == KIO::ERR_CONNECTION_BROKEN ) {
00880 msg = i18n("The connection to the server %1 was unexpectedly closed or timed out. It will be re-established automatically if possible.").
00881 arg( name() );
00882 KMessageBox::information( kapp->activeWindow(), msg, caption, "kmailConnectionBrokenErrorDialog" );
00883
00884 if ( errorCode == KIO::ERR_CONNECTION_BROKEN )
00885 KPIM::BroadcastStatus::instance()->setStatusMsg(
00886 i18n( "The connection to account %1 was broken." ).arg( name() ) );
00887 else if ( errorCode == KIO::ERR_SERVER_TIMEOUT )
00888 KPIM::BroadcastStatus::instance()->setStatusMsg(
00889 i18n( "The connection to account %1 timed out." ).arg( name() ) );
00890 } else {
00891 if ( !errors.isEmpty() )
00892 KMessageBox::detailedError( kapp->activeWindow(), msg, errors.join("\n").prepend("<qt>"), caption );
00893 else
00894 KMessageBox::error( kapp->activeWindow(), msg, caption );
00895 }
00896 }
00897 else {
00898 if ( errors.count() >= 3 ) {
00899 msg = QString( "<qt>") + context + errors[1] + '\n' + errors[2];
00900 caption = errors[0];
00901 }
00902 int ret = KMessageBox::warningContinueCancel( kapp->activeWindow(), msg, caption );
00903 if ( ret == KMessageBox::Cancel ) {
00904 jobsKilled = true;
00905 killAllJobs( false );
00906 }
00907 }
00908 mErrorDialogIsActive = false;
00909 } else {
00910 if ( mErrorDialogIsActive )
00911 kdDebug(5006) << "suppressing error:" << errorMsg << endl;
00912 }
00913 if ( job && !jobsKilled )
00914 removeJob( job );
00915 return !jobsKilled;
00916 }
00917
00918
00919 void ImapAccountBase::cancelMailCheck()
00920 {
00921 QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00922 while ( it != mapJobData.end() ) {
00923 kdDebug(5006) << "cancelMailCheck: job is cancellable: " << (*it).cancellable << endl;
00924 if ( (*it).cancellable ) {
00925 it.key()->kill();
00926 QMap<KIO::Job*, jobData>::Iterator rmit = it;
00927 ++it;
00928 mapJobData.remove( rmit );
00929
00930 mSlave = 0;
00931 } else
00932 ++it;
00933 }
00934
00935 for( QPtrListIterator<FolderJob> it( mJobList ); it.current(); ++it ) {
00936 if ( it.current()->isCancellable() ) {
00937 FolderJob* job = it.current();
00938 job->setPassiveDestructor( true );
00939 mJobList.remove( job );
00940 delete job;
00941 } else
00942 ++it;
00943 }
00944 }
00945
00946
00947
00948 QString ImapAccountBase::jobData::htmlURL() const
00949 {
00950 KURL u( url );
00951 return u.htmlURL();
00952 }
00953
00954
00955 void ImapAccountBase::processNewMailSingleFolder(KMFolder* folder)
00956 {
00957 mFoldersQueuedForChecking.append(folder);
00958 mCheckingSingleFolder = true;
00959 if ( checkingMail() )
00960 {
00961 disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
00962 this, SLOT( slotCheckQueuedFolders() ) );
00963 connect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
00964 this, SLOT( slotCheckQueuedFolders() ) );
00965 } else {
00966 slotCheckQueuedFolders();
00967 }
00968 }
00969
00970
00971 void ImapAccountBase::slotCheckQueuedFolders()
00972 {
00973 disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
00974 this, SLOT( slotCheckQueuedFolders() ) );
00975
00976 QValueList<QGuardedPtr<KMFolder> > mSaveList = mMailCheckFolders;
00977 mMailCheckFolders = mFoldersQueuedForChecking;
00978 kmkernel->acctMgr()->singleCheckMail(this, true);
00979 mMailCheckFolders = mSaveList;
00980 mFoldersQueuedForChecking.clear();
00981 }
00982
00983
00984 bool ImapAccountBase::checkingMail( KMFolder *folder )
00985 {
00986 if (checkingMail() && mFoldersQueuedForChecking.contains(folder))
00987 return true;
00988 return false;
00989 }
00990
00991
00992 void ImapAccountBase::handleBodyStructure( QDataStream & stream, KMMessage * msg,
00993 const AttachmentStrategy *as )
00994 {
00995 mBodyPartList.clear();
00996 mCurrentMsg = msg;
00997
00998 msg->deleteBodyParts();
00999
01000 constructParts( stream, 1, 0, 0, msg->asDwMessage() );
01001 if ( mBodyPartList.count() == 1 )
01002 msg->deleteBodyParts();
01003
01004 if ( !as )
01005 {
01006 kdWarning(5006) << k_funcinfo << " - found no attachment strategy!" << endl;
01007 return;
01008 }
01009
01010
01011 BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as );
01012 visitor->visit( mBodyPartList );
01013 QPtrList<KMMessagePart> parts = visitor->partsToLoad();
01014 delete visitor;
01015 QPtrListIterator<KMMessagePart> it( parts );
01016 KMMessagePart *part;
01017 int partsToLoad = 0;
01018
01019 while ( (part = it.current()) != 0 )
01020 {
01021 ++it;
01022 if ( part->loadPart() )
01023 {
01024 ++partsToLoad;
01025 }
01026 }
01027 if ( (mBodyPartList.count() * 0.5) < partsToLoad )
01028 {
01029
01030
01031 kdDebug(5006) << "Falling back to normal mode" << endl;
01032 FolderJob *job = msg->parent()->createJob(
01033 msg, FolderJob::tGetMessage, 0, "TEXT" );
01034 job->start();
01035 return;
01036 }
01037 it.toFirst();
01038 while ( (part = it.current()) != 0 )
01039 {
01040 ++it;
01041 kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier()
01042 << " (" << part->originalContentTypeStr() << ")" << endl;
01043 if ( part->loadHeaders() )
01044 {
01045 kdDebug(5006) << "load HEADER" << endl;
01046 FolderJob *job = msg->parent()->createJob(
01047 msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" );
01048 job->start();
01049 }
01050 if ( part->loadPart() )
01051 {
01052 kdDebug(5006) << "load Part" << endl;
01053 FolderJob *job = msg->parent()->createJob(
01054 msg, FolderJob::tGetMessage, 0, part->partSpecifier() );
01055 job->start();
01056 }
01057 }
01058 }
01059
01060
01061 void ImapAccountBase::constructParts( QDataStream & stream, int count, KMMessagePart* parentKMPart,
01062 DwBodyPart * parent, const DwMessage * dwmsg )
01063 {
01064 int children;
01065 for (int i = 0; i < count; i++)
01066 {
01067 stream >> children;
01068 KMMessagePart* part = new KMMessagePart( stream );
01069 part->setParent( parentKMPart );
01070 mBodyPartList.append( part );
01071 kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier()
01072 << " of type " << part->originalContentTypeStr() << endl;
01073 DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part );
01074
01075 if ( parent )
01076 {
01077
01078 parent->Body().AddBodyPart( dwpart );
01079 dwpart->Parse();
01080
01081
01082 } else if ( part->partSpecifier() != "0" &&
01083 !part->partSpecifier().endsWith(".HEADER") )
01084 {
01085
01086 dwmsg->Body().AddBodyPart( dwpart );
01087 dwpart->Parse();
01088
01089
01090 } else
01091 dwpart = 0;
01092
01093 if ( !parentKMPart )
01094 parentKMPart = part;
01095
01096 if (children > 0)
01097 {
01098 DwBodyPart* newparent = dwpart;
01099 const DwMessage* newmsg = dwmsg;
01100 if ( part->originalContentTypeStr() == "MESSAGE/RFC822" &&
01101 dwpart->Body().Message() )
01102 {
01103
01104 newparent = 0;
01105 newmsg = dwpart->Body().Message();
01106 }
01107 KMMessagePart* newParentKMPart = part;
01108 if ( part->partSpecifier().endsWith(".HEADER") )
01109 newParentKMPart = parentKMPart;
01110
01111 constructParts( stream, children, newParentKMPart, newparent, newmsg );
01112 }
01113 }
01114 }
01115
01116
01117 void ImapAccountBase::setImapStatus( KMFolder* folder, const QString& path, const QCString& flags )
01118 {
01119
01120 kdDebug(5006) << "setImapStatus path=" << path << " to: " << flags << endl;
01121 KURL url = getUrl();
01122 url.setPath(path);
01123
01124 QByteArray packedArgs;
01125 QDataStream stream( packedArgs, IO_WriteOnly);
01126
01127 stream << (int) 'S' << url << flags;
01128
01129 if ( makeConnection() != Connected )
01130 return;
01131
01132 KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
01133 KIO::Scheduler::assignJobToSlave(slave(), job);
01134 ImapAccountBase::jobData jd( url.url(), folder );
01135 jd.path = path;
01136 insertJob(job, jd);
01137 connect(job, SIGNAL(result(KIO::Job *)),
01138 SLOT(slotSetStatusResult(KIO::Job *)));
01139 }
01140
01141 void ImapAccountBase::slotSetStatusResult(KIO::Job * job)
01142 {
01143 ImapAccountBase::JobIterator it = findJob(job);
01144 if ( it == jobsEnd() ) return;
01145 int errorCode = job->error();
01146 if (errorCode && errorCode != KIO::ERR_CANNOT_OPEN_FOR_WRITING)
01147 {
01148 bool cont = handleJobError( job, i18n( "Error while uploading status of messages to server: " ) + '\n' );
01149 emit imapStatusChanged( (*it).parent, (*it).path, cont );
01150 }
01151 else
01152 {
01153 emit imapStatusChanged( (*it).parent, (*it).path, true );
01154 removeJob(it);
01155 }
01156 }
01157
01158
01159 void ImapAccountBase::setFolder(KMFolder* folder, bool addAccount)
01160 {
01161 if (folder)
01162 {
01163 folder->setSystemLabel(name());
01164 folder->setId(id());
01165 }
01166 NetworkAccount::setFolder(folder, addAccount);
01167 }
01168
01169
01170 void ImapAccountBase::removeJob( JobIterator& it )
01171 {
01172 if( (*it).progressItem ) {
01173 (*it).progressItem->setComplete();
01174 (*it).progressItem = 0;
01175 }
01176 mapJobData.remove( it );
01177 }
01178
01179
01180 void KMail::ImapAccountBase::removeJob( KIO::Job* job )
01181 {
01182 mapJobData.remove( job );
01183 }
01184
01185
01186 KPIM::ProgressItem* ImapAccountBase::listDirProgressItem()
01187 {
01188 if ( !mListDirProgressItem )
01189 {
01190 mListDirProgressItem = ProgressManager::createProgressItem(
01191 "ListDir" + name(),
01192 name(),
01193 i18n("retrieving folders"),
01194 true,
01195 useSSL() || useTLS() );
01196 connect ( mListDirProgressItem,
01197 SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
01198 this,
01199 SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
01200
01201
01202
01203 unsigned int count = folderCount();
01204 mListDirProgressItem->setTotalItems( count + (unsigned int)(count*0.05) );
01205 }
01206 return mListDirProgressItem;
01207 }
01208
01209
01210 unsigned int ImapAccountBase::folderCount() const
01211 {
01212 if ( !rootFolder() || !rootFolder()->folder() || !rootFolder()->folder()->child() )
01213 return 0;
01214 return kmkernel->imapFolderMgr()->folderCount( rootFolder()->folder()->child() );
01215 }
01216
01217
01218 QString ImapAccountBase::addPathToNamespace( const QString& prefix )
01219 {
01220 QString myPrefix = prefix;
01221 if ( !myPrefix.startsWith( "/" ) ) {
01222 myPrefix = "/" + myPrefix;
01223 }
01224 if ( !myPrefix.endsWith( "/" ) ) {
01225 myPrefix += "/";
01226 }
01227
01228 return myPrefix;
01229 }
01230
01231
01232 bool ImapAccountBase::isNamespaceFolder( QString& name )
01233 {
01234 QStringList ns = mNamespaces[OtherUsersNS];
01235 ns += mNamespaces[SharedNS];
01236 ns += mNamespaces[PersonalNS];
01237 QString nameWithDelimiter;
01238 for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
01239 {
01240 nameWithDelimiter = name + delimiterForNamespace( *it );
01241 if ( *it == name || *it == nameWithDelimiter )
01242 return true;
01243 }
01244 return false;
01245 }
01246
01247
01248 ImapAccountBase::nsDelimMap ImapAccountBase::namespacesWithDelimiter()
01249 {
01250 nsDelimMap map;
01251 nsMap::ConstIterator it;
01252 for ( uint i = 0; i < 3; ++i )
01253 {
01254 imapNamespace section = imapNamespace( i );
01255 QStringList namespaces = mNamespaces[section];
01256 namespaceDelim nsDelim;
01257 QStringList::Iterator lit;
01258 for ( lit = namespaces.begin(); lit != namespaces.end(); ++lit )
01259 {
01260 nsDelim[*lit] = delimiterForNamespace( *lit );
01261 }
01262 map[section] = nsDelim;
01263 }
01264 return map;
01265 }
01266
01267
01268 QString ImapAccountBase::createImapPath( const QString& parent,
01269 const QString& folderName )
01270 {
01271 kdDebug(5006) << "createImapPath parent="<<parent<<", folderName="<<folderName<<endl;
01272 QString newName = parent;
01273
01274 if ( newName.endsWith("/") ) {
01275 newName = newName.left( newName.length() - 1 );
01276 }
01277
01278 QString delim = delimiterForNamespace( newName );
01279
01280 if ( delim.isEmpty() ) {
01281 delim = "/";
01282 }
01283 if ( !newName.endsWith( delim ) && !folderName.startsWith( delim ) ) {
01284 newName = newName + delim;
01285 }
01286 newName = newName + folderName;
01287
01288 if ( !newName.endsWith("/") ) {
01289 newName = newName + "/";
01290 }
01291
01292 return newName;
01293 }
01294
01295
01296 QString ImapAccountBase::createImapPath( FolderStorage* parent,
01297 const QString& folderName )
01298 {
01299 QString path;
01300 if ( parent->folderType() == KMFolderTypeImap ) {
01301 path = static_cast<KMFolderImap*>( parent )->imapPath();
01302 } else if ( parent->folderType() == KMFolderTypeCachedImap ) {
01303 path = static_cast<KMFolderCachedImap*>( parent )->imapPath();
01304 } else {
01305
01306 return path;
01307 }
01308
01309 return createImapPath( path, folderName );
01310 }
01311
01312 }
01313
01314 #include "imapaccountbase.moc"