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 kmkernel->acctMgr()->singleCheckMail(this, true);
00980 mMailCheckFolders = mSaveList;
00981 mFoldersQueuedForChecking.clear();
00982 }
00983
00984
00985 bool ImapAccountBase::checkingMail( KMFolder *folder )
00986 {
00987 if (checkingMail() && mFoldersQueuedForChecking.contains(folder))
00988 return true;
00989 return false;
00990 }
00991
00992
00993 void ImapAccountBase::handleBodyStructure( QDataStream & stream, KMMessage * msg,
00994 const AttachmentStrategy *as )
00995 {
00996 mBodyPartList.clear();
00997 mCurrentMsg = msg;
00998
00999 msg->deleteBodyParts();
01000
01001 constructParts( stream, 1, 0, 0, msg->asDwMessage() );
01002 if ( mBodyPartList.count() == 1 )
01003 msg->deleteBodyParts();
01004
01005 if ( !as )
01006 {
01007 kdWarning(5006) << k_funcinfo << " - found no attachment strategy!" << endl;
01008 return;
01009 }
01010
01011
01012 BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as );
01013 visitor->visit( mBodyPartList );
01014 QPtrList<KMMessagePart> parts = visitor->partsToLoad();
01015 delete visitor;
01016 QPtrListIterator<KMMessagePart> it( parts );
01017 KMMessagePart *part;
01018 int partsToLoad = 0;
01019
01020 while ( (part = it.current()) != 0 )
01021 {
01022 ++it;
01023 if ( part->loadPart() )
01024 {
01025 ++partsToLoad;
01026 }
01027 }
01028 if ( (mBodyPartList.count() * 0.5) < partsToLoad )
01029 {
01030
01031
01032 kdDebug(5006) << "Falling back to normal mode" << endl;
01033 FolderJob *job = msg->parent()->createJob(
01034 msg, FolderJob::tGetMessage, 0, "TEXT" );
01035 job->start();
01036 return;
01037 }
01038 it.toFirst();
01039 while ( (part = it.current()) != 0 )
01040 {
01041 ++it;
01042 kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier()
01043 << " (" << part->originalContentTypeStr() << ")" << endl;
01044 if ( part->loadHeaders() )
01045 {
01046 kdDebug(5006) << "load HEADER" << endl;
01047 FolderJob *job = msg->parent()->createJob(
01048 msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" );
01049 job->start();
01050 }
01051 if ( part->loadPart() )
01052 {
01053 kdDebug(5006) << "load Part" << endl;
01054 FolderJob *job = msg->parent()->createJob(
01055 msg, FolderJob::tGetMessage, 0, part->partSpecifier() );
01056 job->start();
01057 }
01058 }
01059 }
01060
01061
01062 void ImapAccountBase::constructParts( QDataStream & stream, int count, KMMessagePart* parentKMPart,
01063 DwBodyPart * parent, const DwMessage * dwmsg )
01064 {
01065 int children;
01066 for (int i = 0; i < count; i++)
01067 {
01068 stream >> children;
01069 KMMessagePart* part = new KMMessagePart( stream );
01070 part->setParent( parentKMPart );
01071 mBodyPartList.append( part );
01072 kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier()
01073 << " of type " << part->originalContentTypeStr() << endl;
01074 DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part );
01075
01076 if ( parent )
01077 {
01078
01079 parent->Body().AddBodyPart( dwpart );
01080 dwpart->Parse();
01081
01082
01083 } else if ( part->partSpecifier() != "0" &&
01084 !part->partSpecifier().endsWith(".HEADER") )
01085 {
01086
01087 dwmsg->Body().AddBodyPart( dwpart );
01088 dwpart->Parse();
01089
01090
01091 } else
01092 dwpart = 0;
01093
01094 if ( !parentKMPart )
01095 parentKMPart = part;
01096
01097 if (children > 0)
01098 {
01099 DwBodyPart* newparent = dwpart;
01100 const DwMessage* newmsg = dwmsg;
01101 if ( part->originalContentTypeStr() == "MESSAGE/RFC822" &&
01102 dwpart->Body().Message() )
01103 {
01104
01105 newparent = 0;
01106 newmsg = dwpart->Body().Message();
01107 }
01108 KMMessagePart* newParentKMPart = part;
01109 if ( part->partSpecifier().endsWith(".HEADER") )
01110 newParentKMPart = parentKMPart;
01111
01112 constructParts( stream, children, newParentKMPart, newparent, newmsg );
01113 }
01114 }
01115 }
01116
01117
01118 void ImapAccountBase::setImapStatus( KMFolder* folder, const QString& path, const QCString& flags )
01119 {
01120
01121 kdDebug(5006) << "setImapStatus path=" << path << " to: " << flags << endl;
01122 KURL url = getUrl();
01123 url.setPath(path);
01124
01125 QByteArray packedArgs;
01126 QDataStream stream( packedArgs, IO_WriteOnly);
01127
01128 stream << (int) 'S' << url << flags;
01129
01130 if ( makeConnection() != Connected )
01131 return;
01132
01133 KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
01134 KIO::Scheduler::assignJobToSlave(slave(), job);
01135 ImapAccountBase::jobData jd( url.url(), folder );
01136 jd.path = path;
01137 insertJob(job, jd);
01138 connect(job, SIGNAL(result(KIO::Job *)),
01139 SLOT(slotSetStatusResult(KIO::Job *)));
01140 }
01141
01142 void ImapAccountBase::slotSetStatusResult(KIO::Job * job)
01143 {
01144 ImapAccountBase::JobIterator it = findJob(job);
01145 if ( it == jobsEnd() ) return;
01146 int errorCode = job->error();
01147 if (errorCode && errorCode != KIO::ERR_CANNOT_OPEN_FOR_WRITING)
01148 {
01149 bool cont = handleJobError( job, i18n( "Error while uploading status of messages to server: " ) + '\n' );
01150 emit imapStatusChanged( (*it).parent, (*it).path, cont );
01151 }
01152 else
01153 {
01154 emit imapStatusChanged( (*it).parent, (*it).path, true );
01155 removeJob(it);
01156 }
01157 }
01158
01159
01160 void ImapAccountBase::setFolder(KMFolder* folder, bool addAccount)
01161 {
01162 if (folder)
01163 {
01164 folder->setSystemLabel(name());
01165 folder->setId(id());
01166 }
01167 NetworkAccount::setFolder(folder, addAccount);
01168 }
01169
01170
01171 void ImapAccountBase::removeJob( JobIterator& it )
01172 {
01173 if( (*it).progressItem ) {
01174 (*it).progressItem->setComplete();
01175 (*it).progressItem = 0;
01176 }
01177 mapJobData.remove( it );
01178 }
01179
01180
01181 void KMail::ImapAccountBase::removeJob( KIO::Job* job )
01182 {
01183 mapJobData.remove( job );
01184 }
01185
01186
01187 KPIM::ProgressItem* ImapAccountBase::listDirProgressItem()
01188 {
01189 if ( !mListDirProgressItem )
01190 {
01191 mListDirProgressItem = ProgressManager::createProgressItem(
01192 "ListDir" + name(),
01193 name(),
01194 i18n("retrieving folders"),
01195 true,
01196 useSSL() || useTLS() );
01197 connect ( mListDirProgressItem,
01198 SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
01199 this,
01200 SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
01201
01202
01203
01204 unsigned int count = folderCount();
01205 mListDirProgressItem->setTotalItems( count + (unsigned int)(count*0.05) );
01206 }
01207 return mListDirProgressItem;
01208 }
01209
01210
01211 unsigned int ImapAccountBase::folderCount() const
01212 {
01213 if ( !rootFolder() || !rootFolder()->folder() || !rootFolder()->folder()->child() )
01214 return 0;
01215 return kmkernel->imapFolderMgr()->folderCount( rootFolder()->folder()->child() );
01216 }
01217
01218
01219 QString ImapAccountBase::addPathToNamespace( const QString& prefix )
01220 {
01221 QString myPrefix = prefix;
01222 if ( !myPrefix.startsWith( "/" ) ) {
01223 myPrefix = "/" + myPrefix;
01224 }
01225 if ( !myPrefix.endsWith( "/" ) ) {
01226 myPrefix += "/";
01227 }
01228
01229 return myPrefix;
01230 }
01231
01232
01233 bool ImapAccountBase::isNamespaceFolder( QString& name )
01234 {
01235 QStringList ns = mNamespaces[OtherUsersNS];
01236 ns += mNamespaces[SharedNS];
01237 ns += mNamespaces[PersonalNS];
01238 QString nameWithDelimiter;
01239 for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
01240 {
01241 nameWithDelimiter = name + delimiterForNamespace( *it );
01242 if ( *it == name || *it == nameWithDelimiter )
01243 return true;
01244 }
01245 return false;
01246 }
01247
01248
01249 ImapAccountBase::nsDelimMap ImapAccountBase::namespacesWithDelimiter()
01250 {
01251 nsDelimMap map;
01252 nsMap::ConstIterator it;
01253 for ( uint i = 0; i < 3; ++i )
01254 {
01255 imapNamespace section = imapNamespace( i );
01256 QStringList namespaces = mNamespaces[section];
01257 namespaceDelim nsDelim;
01258 QStringList::Iterator lit;
01259 for ( lit = namespaces.begin(); lit != namespaces.end(); ++lit )
01260 {
01261 nsDelim[*lit] = delimiterForNamespace( *lit );
01262 }
01263 map[section] = nsDelim;
01264 }
01265 return map;
01266 }
01267
01268
01269 QString ImapAccountBase::createImapPath( const QString& parent,
01270 const QString& folderName )
01271 {
01272 kdDebug(5006) << "createImapPath parent="<<parent<<", folderName="<<folderName<<endl;
01273 QString newName = parent;
01274
01275 if ( newName.endsWith("/") ) {
01276 newName = newName.left( newName.length() - 1 );
01277 }
01278
01279 QString delim = delimiterForNamespace( newName );
01280
01281 if ( delim.isEmpty() ) {
01282 delim = "/";
01283 }
01284 if ( !newName.endsWith( delim ) && !folderName.startsWith( delim ) ) {
01285 newName = newName + delim;
01286 }
01287 newName = newName + folderName;
01288
01289 if ( !newName.endsWith("/") ) {
01290 newName = newName + "/";
01291 }
01292
01293 return newName;
01294 }
01295
01296
01297 QString ImapAccountBase::createImapPath( FolderStorage* parent,
01298 const QString& folderName )
01299 {
01300 QString path;
01301 if ( parent->folderType() == KMFolderTypeImap ) {
01302 path = static_cast<KMFolderImap*>( parent )->imapPath();
01303 } else if ( parent->folderType() == KMFolderTypeCachedImap ) {
01304 path = static_cast<KMFolderCachedImap*>( parent )->imapPath();
01305 } else {
01306
01307 return path;
01308 }
01309
01310 return createImapPath( path, folderName );
01311 }
01312
01313 }
01314
01315 #include "imapaccountbase.moc"