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