kmail

imapaccountbase.cpp

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 //using KIO::Scheduler; // use FQN below
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   // Ctor and Dtor
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       mOnlyLocallySubscribedFolders( false ),
00091       mLoadOnDemand( true ),
00092       mListOnlyOpenFolders( false ),
00093       mProgressEnabled( false ),
00094       mErrorDialogIsActive( false ),
00095       mPasswordDialogIsActive( false ),
00096       mACLSupport( true ),
00097       mAnnotationSupport( true ),
00098       mQuotaSupport( true ),
00099       mSlaveConnected( false ),
00100       mSlaveConnectionError( false ),
00101       mCheckingSingleFolder( false ),
00102       mListDirProgressItem( 0 )
00103   {
00104     mPort = imapDefaultPort;
00105     mBodyPartList.setAutoDelete(true);
00106     KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
00107                             this, SLOT(slotSchedulerSlaveError(KIO::Slave *, int, const QString &)));
00108     KIO::Scheduler::connect(SIGNAL(slaveConnected(KIO::Slave *)),
00109                             this, SLOT(slotSchedulerSlaveConnected(KIO::Slave *)));
00110     connect(&mNoopTimer, SIGNAL(timeout()), SLOT(slotNoopTimeout()));
00111     connect(&mIdleTimer, SIGNAL(timeout()), SLOT(slotIdleTimeout()));
00112   }
00113 
00114   ImapAccountBase::~ImapAccountBase() {
00115     kdWarning( mSlave, 5006 )
00116       << "slave should have been destroyed by subclass!" << endl;
00117   }
00118 
00119   void ImapAccountBase::init() {
00120     mAutoExpunge = true;
00121     mHiddenFolders = false;
00122     mOnlySubscribedFolders = false;
00123     mOnlyLocallySubscribedFolders = false;
00124     mLoadOnDemand = true;
00125     mListOnlyOpenFolders = false;
00126     mProgressEnabled = false;
00127   }
00128 
00129   void ImapAccountBase::pseudoAssign( const KMAccount * a ) {
00130     NetworkAccount::pseudoAssign( a );
00131 
00132     const ImapAccountBase * i = dynamic_cast<const ImapAccountBase*>( a );
00133     if ( !i ) return;
00134 
00135     setAutoExpunge( i->autoExpunge() );
00136     setHiddenFolders( i->hiddenFolders() );
00137     setOnlySubscribedFolders( i->onlySubscribedFolders() );
00138     setOnlyLocallySubscribedFolders( i->onlyLocallySubscribedFolders() );
00139     setLoadOnDemand( i->loadOnDemand() );
00140     setListOnlyOpenFolders( i->listOnlyOpenFolders() );
00141     setNamespaces( i->namespaces() );
00142     setNamespaceToDelimiter( i->namespaceToDelimiter() );
00143     localBlacklistFromStringList( i->locallyBlacklistedFolders() );
00144   }
00145 
00146   unsigned short int ImapAccountBase::defaultPort() const {
00147     return imapDefaultPort;
00148   }
00149 
00150   QString ImapAccountBase::protocol() const {
00151     return useSSL() ? IMAP_SSL_PROTOCOL : IMAP_PROTOCOL;
00152   }
00153 
00154   //
00155   //
00156   // Getters and Setters
00157   //
00158   //
00159 
00160   void ImapAccountBase::setAutoExpunge( bool expunge ) {
00161     mAutoExpunge = expunge;
00162   }
00163 
00164   void ImapAccountBase::setHiddenFolders( bool show ) {
00165     mHiddenFolders = show;
00166   }
00167 
00168   void ImapAccountBase::setOnlySubscribedFolders( bool show ) {
00169     mOnlySubscribedFolders = show;
00170   }
00171 
00172   void ImapAccountBase::setOnlyLocallySubscribedFolders( bool show ) {
00173     mOnlyLocallySubscribedFolders = show;
00174   }
00175 
00176   void ImapAccountBase::setLoadOnDemand( bool load ) {
00177     mLoadOnDemand = load;
00178   }
00179 
00180   void ImapAccountBase::setListOnlyOpenFolders( bool only ) {
00181     mListOnlyOpenFolders = only;
00182   }
00183 
00184   //
00185   //
00186   // read/write config
00187   //
00188   //
00189 
00190   void ImapAccountBase::readConfig( /*const*/ KConfig/*Base*/ & config ) {
00191     NetworkAccount::readConfig( config );
00192 
00193     setAutoExpunge( config.readBoolEntry( "auto-expunge", false ) );
00194     setHiddenFolders( config.readBoolEntry( "hidden-folders", false ) );
00195     setOnlySubscribedFolders( config.readBoolEntry( "subscribed-folders", false ) );
00196     setOnlyLocallySubscribedFolders( config.readBoolEntry( "locally-subscribed-folders", false ) );
00197     setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) );
00198     setListOnlyOpenFolders( config.readBoolEntry( "listOnlyOpenFolders", false ) );
00199     // read namespaces
00200     nsMap map;
00201     QStringList list = config.readListEntry( QString::number( PersonalNS ) );
00202     if ( !list.isEmpty() )
00203       map[PersonalNS] = list.gres( "\"", "" );
00204     list = config.readListEntry( QString::number( OtherUsersNS ) );
00205     if ( !list.isEmpty() )
00206       map[OtherUsersNS] = list.gres( "\"", "" );
00207     list = config.readListEntry( QString::number( SharedNS ) );
00208     if ( !list.isEmpty() )
00209       map[SharedNS] = list.gres( "\"", "" );
00210     setNamespaces( map );
00211     // read namespace - delimiter
00212     namespaceDelim entries = config.entryMap( config.group() );
00213     namespaceDelim namespaceToDelimiter;
00214     for ( namespaceDelim::ConstIterator it = entries.begin(); 
00215           it != entries.end(); ++it ) {
00216       if ( it.key().startsWith( "Namespace:" ) ) {
00217         QString key = it.key().right( it.key().length() - 10 );
00218         namespaceToDelimiter[key] = it.data();
00219       }
00220     }
00221     setNamespaceToDelimiter( namespaceToDelimiter );
00222     mOldPrefix = config.readEntry( "prefix" );
00223     if ( !mOldPrefix.isEmpty() ) {
00224       makeConnection();
00225     }
00226     localBlacklistFromStringList( config.readListEntry( "locallyUnsubscribedFolders" ) );
00227   }
00228 
00229   void ImapAccountBase::writeConfig( KConfig/*Base*/ & config ) /*const*/ {
00230     NetworkAccount::writeConfig( config );
00231 
00232     config.writeEntry( "auto-expunge", autoExpunge() );
00233     config.writeEntry( "hidden-folders", hiddenFolders() );
00234     config.writeEntry( "subscribed-folders", onlySubscribedFolders() );
00235     config.writeEntry( "locally-subscribed-folders", onlyLocallySubscribedFolders() );
00236     config.writeEntry( "loadondemand", loadOnDemand() );
00237     config.writeEntry( "listOnlyOpenFolders", listOnlyOpenFolders() );
00238     QString data;
00239     for ( nsMap::Iterator it = mNamespaces.begin(); it != mNamespaces.end(); ++it ) {
00240       if ( !it.data().isEmpty() ) {
00241         data = "\"" + it.data().join("\",\"") + "\"";
00242         config.writeEntry( QString::number( it.key() ), data );
00243       }
00244     }
00245     QString key;
00246     for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin(); 
00247           it != mNamespaceToDelimiter.end(); ++it ) {
00248       key = "Namespace:" + it.key();
00249       config.writeEntry( key, it.data() );
00250     }
00251     config.writeEntry( "locallyUnsubscribedFolders", locallyBlacklistedFolders() );
00252   }
00253 
00254   //
00255   //
00256   // Network processing
00257   //
00258   //
00259 
00260   MetaData ImapAccountBase::slaveConfig() const {
00261     MetaData m = NetworkAccount::slaveConfig();
00262 
00263     m.insert( "auth", auth() );
00264     if ( autoExpunge() )
00265       m.insert( "expunge", "auto" );
00266 
00267     return m;
00268   }
00269 
00270   ImapAccountBase::ConnectionState ImapAccountBase::makeConnection() 
00271   {
00272     if ( mSlave && mSlaveConnected ) {
00273       return Connected;
00274     }
00275     if ( mPasswordDialogIsActive ) return Connecting;
00276 
00277     if( mAskAgain || ( ( passwd().isEmpty() || login().isEmpty() ) &&
00278                          auth() != "GSSAPI" ) ) {
00279 
00280       Q_ASSERT( !mSlave ); // disconnected on 'wrong login' error already, or first try
00281       QString log = login();
00282       QString pass = passwd();
00283       // We init "store" to true to indicate that we want to have the
00284       // "keep password" checkbox. Then, we set [Passwords]Keep to
00285       // storePasswd(), so that the checkbox in the dialog will be
00286       // init'ed correctly:
00287       KConfigGroup passwords( KGlobal::config(), "Passwords" );
00288       passwords.writeEntry( "Keep", storePasswd() );
00289       QString msg = i18n("You need to supply a username and a password to "
00290              "access this mailbox.");
00291       mPasswordDialogIsActive = true;
00292       
00293       PasswordDialog dlg( msg, log, true /* store pw */, true, KMKernel::self()->mainWin() );
00294       dlg.setPlainCaption( i18n("Authorization Dialog") );
00295       dlg.addCommentLine( i18n("Account:"), name() );
00296       int ret = dlg.exec();
00297       if (ret != QDialog::Accepted ) {
00298         mPasswordDialogIsActive = false;
00299         mAskAgain = false;
00300         emit connectionResult( KIO::ERR_USER_CANCELED, QString::null );
00301         return Error;
00302       }
00303       mPasswordDialogIsActive = false;
00304       // The user has been given the chance to change login and
00305       // password, so copy both from the dialog:
00306       setPasswd( dlg.password(), dlg.keepPassword() );
00307       setLogin( dlg.username() );
00308       mAskAgain = false;
00309     }
00310     // already waiting for a connection?
00311     if ( mSlave && !mSlaveConnected ) return Connecting;
00312 
00313     mSlaveConnected = false;
00314     mSlave = KIO::Scheduler::getConnectedSlave( getUrl(), slaveConfig() );
00315     if ( !mSlave ) {
00316       KMessageBox::error(0, i18n("Could not start process for %1.")
00317              .arg( getUrl().protocol() ) );
00318       return Error;
00319     }
00320     if ( mSlave->isConnected() ) {
00321       slotSchedulerSlaveConnected( mSlave );
00322       return Connected;
00323     }
00324 
00325     return Connecting;
00326   }
00327 
00328   bool ImapAccountBase::handleJobError( KIO::Job *job, const QString& context, bool abortSync )
00329   {
00330     JobIterator it = findJob( job );
00331     if ( it != jobsEnd() && (*it).progressItem )
00332     {
00333       (*it).progressItem->setComplete();
00334       (*it).progressItem = 0;
00335     }
00336     return handleError( job->error(), job->errorText(), job, context, abortSync );
00337   }
00338 
00339   // Called when we're really all done.
00340   void ImapAccountBase::postProcessNewMail( bool showStatusMsg ) {
00341     setCheckingMail(false);
00342     int newMails = 0;
00343     if ( mCountUnread > 0 && mCountUnread > mCountLastUnread ) {
00344       newMails = mCountUnread  - mCountLastUnread;
00345       mCountLastUnread = mCountUnread;
00346       mCountUnread = 0;
00347       checkDone( true, CheckOK );
00348     } else {
00349       mCountUnread = 0;
00350       checkDone( false, CheckOK );
00351     }
00352     if ( showStatusMsg )
00353       BroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
00354           name(), newMails);
00355   }
00356 
00357   //-----------------------------------------------------------------------------
00358   void ImapAccountBase::changeSubscription( bool subscribe, const QString& imapPath )
00359   {
00360     // change the subscription of the folder
00361     KURL url = getUrl();
00362     url.setPath(imapPath);
00363 
00364     QByteArray packedArgs;
00365     QDataStream stream( packedArgs, IO_WriteOnly);
00366 
00367     if (subscribe)
00368       stream << (int) 'u' << url;
00369     else
00370       stream << (int) 'U' << url;
00371 
00372     // create the KIO-job
00373     if ( makeConnection() != Connected ) 
00374       return;// ## doesn't handle Connecting
00375     KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
00376     KIO::Scheduler::assignJobToSlave(mSlave, job);
00377     jobData jd( url.url(), NULL );
00378     // a bit of a hack to save one slot
00379     if (subscribe) jd.onlySubscribed = true;
00380     else jd.onlySubscribed = false;
00381     insertJob(job, jd);
00382 
00383     connect(job, SIGNAL(result(KIO::Job *)),
00384         SLOT(slotSubscriptionResult(KIO::Job *)));
00385   }
00386 
00387   //-----------------------------------------------------------------------------
00388   void ImapAccountBase::slotSubscriptionResult( KIO::Job * job )
00389   {
00390     // result of a subscription-job
00391     JobIterator it = findJob( job );
00392     if ( it == jobsEnd() ) return;
00393     bool onlySubscribed = (*it).onlySubscribed;
00394     QString path = static_cast<KIO::SimpleJob*>(job)->url().path();
00395     if (job->error())
00396     {
00397       handleJobError( job, i18n( "Error while trying to subscribe to %1:" ).arg( path ) + '\n' );
00398       // ## emit subscriptionChanged here in case anyone needs it to support continue/cancel
00399     }
00400     else
00401     {
00402       emit subscriptionChanged( path, onlySubscribed );
00403       if (mSlave) removeJob(job);
00404     }
00405   }
00406 
00407   //-----------------------------------------------------------------------------
00408   // TODO imapPath can be removed once parent can be a KMFolderImapBase or whatever
00409   void ImapAccountBase::getUserRights( KMFolder* parent, const QString& imapPath )
00410   {
00411     // There isn't much point in asking the server about a user's rights on his own inbox,
00412     // it might not be the effective permissions (at least with Cyrus, one can admin his own inbox,
00413     // even after a SETACL that removes the admin permissions. Other imap servers apparently
00414     // don't even allow removing one's own admin permission, so this code won't hurt either).
00415     if ( imapPath == "/INBOX/" ) {
00416       if ( parent->folderType() == KMFolderTypeImap )
00417         static_cast<KMFolderImap*>( parent->storage() )->setUserRights( ACLJobs::All );
00418       else if ( parent->folderType() == KMFolderTypeCachedImap )
00419         static_cast<KMFolderCachedImap*>( parent->storage() )->setUserRights( ACLJobs::All );
00420       emit receivedUserRights( parent ); // warning, you need to connect first to get that one
00421       return;
00422     }
00423 
00424     KURL url = getUrl();
00425     url.setPath(imapPath);
00426 
00427     ACLJobs::GetUserRightsJob* job = ACLJobs::getUserRights( mSlave, url );
00428 
00429     jobData jd( url.url(), parent );
00430     jd.cancellable = true;
00431     insertJob(job, jd);
00432 
00433     connect(job, SIGNAL(result(KIO::Job *)),
00434             SLOT(slotGetUserRightsResult(KIO::Job *)));
00435   }
00436 
00437   void ImapAccountBase::slotGetUserRightsResult( KIO::Job* _job )
00438   {
00439     ACLJobs::GetUserRightsJob* job = static_cast<ACLJobs::GetUserRightsJob *>( _job );
00440     JobIterator it = findJob( job );
00441     if ( it == jobsEnd() ) return;
00442 
00443     KMFolder* folder = (*it).parent;
00444     if ( job->error() ) {
00445       if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION ) // that's when the imap server doesn't support ACLs
00446           mACLSupport = false;
00447       else
00448         kdWarning(5006) << "slotGetUserRightsResult: " << job->errorString() << endl;
00449     } else {
00450 #ifndef NDEBUG
00451       //kdDebug(5006) << "User Rights: " << ACLJobs::permissionsToString( job->permissions() ) << endl;
00452 #endif
00453       // Store the permissions
00454       if ( folder->folderType() == KMFolderTypeImap )
00455         static_cast<KMFolderImap*>( folder->storage() )->setUserRights( job->permissions() );
00456       else if ( folder->folderType() == KMFolderTypeCachedImap )
00457         static_cast<KMFolderCachedImap*>( folder->storage() )->setUserRights( job->permissions() );
00458     }
00459     if (mSlave) removeJob(job);
00460     emit receivedUserRights( folder );
00461   }
00462 
00463   //-----------------------------------------------------------------------------
00464   void ImapAccountBase::getACL( KMFolder* parent, const QString& imapPath )
00465   {
00466     KURL url = getUrl();
00467     url.setPath(imapPath);
00468 
00469     ACLJobs::GetACLJob* job = ACLJobs::getACL( mSlave, url );
00470     jobData jd( url.url(), parent );
00471     jd.cancellable = true;
00472     insertJob(job, jd);
00473 
00474     connect(job, SIGNAL(result(KIO::Job *)),
00475             SLOT(slotGetACLResult(KIO::Job *)));
00476   }
00477 
00478   void ImapAccountBase::slotGetACLResult( KIO::Job* _job )
00479   {
00480     ACLJobs::GetACLJob* job = static_cast<ACLJobs::GetACLJob *>( _job );
00481     JobIterator it = findJob( job );
00482     if ( it == jobsEnd() ) return;
00483 
00484     KMFolder* folder = (*it).parent;
00485     emit receivedACL( folder, job, job->entries() );
00486     if (mSlave) removeJob(job);
00487   }
00488 
00489   //-----------------------------------------------------------------------------
00490   // Do not remove imapPath, FolderDiaQuotaTab needs to call this with parent==0.
00491   void ImapAccountBase::getStorageQuotaInfo( KMFolder* parent, const QString& imapPath )
00492   {
00493     if ( !mSlave ) return;
00494     KURL url = getUrl();
00495     url.setPath(imapPath);
00496 
00497     QuotaJobs::GetStorageQuotaJob* job = QuotaJobs::getStorageQuota( mSlave, url );
00498     jobData jd( url.url(), parent );
00499     jd.cancellable = true;
00500     insertJob(job, jd);
00501 
00502     connect(job, SIGNAL(result(KIO::Job *)),
00503             SLOT(slotGetStorageQuotaInfoResult(KIO::Job *)));
00504   }
00505 
00506   void ImapAccountBase::slotGetStorageQuotaInfoResult( KIO::Job* _job )
00507   {
00508     QuotaJobs::GetStorageQuotaJob* job = static_cast<QuotaJobs::GetStorageQuotaJob *>( _job );
00509     JobIterator it = findJob( job );
00510     if ( it == jobsEnd() ) return;
00511     if ( job->error() && job->error() == KIO::ERR_UNSUPPORTED_ACTION )
00512       setHasNoQuotaSupport();
00513 
00514     KMFolder* folder = (*it).parent; // can be 0
00515     emit receivedStorageQuotaInfo( folder, job, job->storageQuotaInfo() );
00516     if (mSlave) removeJob(job);
00517   }
00518 
00519   void ImapAccountBase::slotNoopTimeout()
00520   {
00521     if ( mSlave ) {
00522       QByteArray packedArgs;
00523       QDataStream stream( packedArgs, IO_WriteOnly );
00524 
00525       stream << ( int ) 'N';
00526 
00527       KIO::SimpleJob *job = KIO::special( getUrl(), packedArgs, false );
00528       KIO::Scheduler::assignJobToSlave(mSlave, job);
00529       connect( job, SIGNAL(result( KIO::Job * ) ),
00530           this, SLOT( slotSimpleResult( KIO::Job * ) ) );
00531     } else {
00532       /* Stop the timer, we have disconnected. We have to make sure it is
00533          started again when a new slave appears. */
00534       mNoopTimer.stop();
00535     }
00536   }
00537 
00538   void ImapAccountBase::slotIdleTimeout()
00539   {
00540     if ( mSlave ) {
00541       KIO::Scheduler::disconnectSlave(mSlave);
00542       mSlave = 0;
00543       mSlaveConnected = false;
00544       /* As for the noop timer, we need to make sure this one is started
00545          again when a new slave goes up. */
00546       mIdleTimer.stop();
00547     }
00548   }
00549 
00550   void ImapAccountBase::slotAbortRequested( KPIM::ProgressItem* item )
00551   {
00552     if ( item )
00553       item->setComplete();
00554     killAllJobs();
00555   }
00556 
00557 
00558   //-----------------------------------------------------------------------------
00559   void ImapAccountBase::slotSchedulerSlaveError(KIO::Slave *aSlave, int errorCode,
00560       const QString &errorMsg)
00561   {
00562     if (aSlave != mSlave) return;
00563     handleError( errorCode, errorMsg, 0, QString::null, true );
00564     if ( mAskAgain )
00565       if ( makeConnection() != ImapAccountBase::Error )
00566         return;
00567 
00568     if ( !mSlaveConnected ) {
00569       mSlaveConnectionError = true;
00570       resetConnectionList( this );
00571       if ( mSlave )
00572       {
00573         KIO::Scheduler::disconnectSlave( slave() );
00574         mSlave = 0;
00575       }
00576     }
00577     emit connectionResult( errorCode, errorMsg );
00578   }
00579 
00580   //-----------------------------------------------------------------------------
00581   void ImapAccountBase::slotSchedulerSlaveConnected(KIO::Slave *aSlave)
00582   {
00583     if (aSlave != mSlave) return;
00584     mSlaveConnected = true;
00585     mNoopTimer.start( 60000 ); // make sure we start sending noops
00586     emit connectionResult( 0, QString::null ); // success
00587 
00588     if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
00589       connect( this, SIGNAL( namespacesFetched( const ImapAccountBase::nsDelimMap& ) ),
00590           this, SLOT( slotSaveNamespaces( const ImapAccountBase::nsDelimMap& ) ) );
00591       getNamespaces();
00592     }
00593 
00594     // get capabilities
00595     QByteArray packedArgs;
00596     QDataStream stream( packedArgs, IO_WriteOnly);
00597     stream << (int) 'c';
00598     KIO::SimpleJob *job = KIO::special( getUrl(), packedArgs, false );
00599     KIO::Scheduler::assignJobToSlave( mSlave, job );
00600     connect( job, SIGNAL(infoMessage(KIO::Job*, const QString&)),
00601        SLOT(slotCapabilitiesResult(KIO::Job*, const QString&)) );
00602   }
00603 
00604   //-----------------------------------------------------------------------------
00605   void ImapAccountBase::slotCapabilitiesResult( KIO::Job*, const QString& result )
00606   {
00607     mCapabilities = QStringList::split(' ', result.lower() );
00608     kdDebug(5006) << "capabilities:" << mCapabilities << endl;
00609   }
00610 
00611   //-----------------------------------------------------------------------------
00612   void ImapAccountBase::getNamespaces()
00613   {
00614     disconnect( this, SIGNAL( connectionResult(int, const QString&) ),
00615           this, SLOT( getNamespaces() ) );
00616     if ( makeConnection() != Connected || !mSlave ) {
00617       kdDebug(5006) << "getNamespaces - wait for connection" << endl;
00618       if ( mNamespaces.isEmpty() || mNamespaceToDelimiter.isEmpty() ) {
00619         // when the connection is established slotSchedulerSlaveConnected notifies us
00620       } else {
00621         // getNamespaces was called by someone else
00622         connect( this, SIGNAL( connectionResult(int, const QString&) ),
00623             this, SLOT( getNamespaces() ) );
00624       }
00625       return;
00626     }
00627     
00628     QByteArray packedArgs;
00629     QDataStream stream( packedArgs, IO_WriteOnly);
00630     stream << (int) 'n';
00631     jobData jd;
00632     jd.total = 1; jd.done = 0; jd.cancellable = true;
00633     jd.progressItem = ProgressManager::createProgressItem( 
00634         ProgressManager::getUniqueID(),
00635         i18n("Retrieving Namespaces"),
00636         QString::null, true, useSSL() || useTLS() );
00637     jd.progressItem->setTotalItems( 1 );
00638     connect ( jd.progressItem,
00639         SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
00640         this,
00641         SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
00642     KIO::SimpleJob *job = KIO::special( getUrl(), packedArgs, false );
00643     KIO::Scheduler::assignJobToSlave( mSlave, job );
00644     insertJob( job, jd );
00645     connect( job, SIGNAL( infoMessage(KIO::Job*, const QString&) ),
00646         SLOT( slotNamespaceResult(KIO::Job*, const QString&) ) );
00647   }
00648 
00649   //-----------------------------------------------------------------------------
00650   void ImapAccountBase::slotNamespaceResult( KIO::Job* job, const QString& str )
00651   {
00652     JobIterator it = findJob( job );
00653     if ( it == jobsEnd() ) return;
00654 
00655     nsDelimMap map;
00656     namespaceDelim nsDelim;
00657     QStringList ns = QStringList::split( ",", str );
00658     for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it ) {
00659       // split, allow empty parts as we can get empty namespaces
00660       QStringList parts = QStringList::split( "=", *it, true );
00661       imapNamespace section = imapNamespace( parts[0].toInt() );
00662       if ( map.contains( section ) ) {
00663         nsDelim = map[section];
00664       } else {
00665         nsDelim.clear();
00666       }
00667       // map namespace to delimiter
00668       nsDelim[parts[1]] = parts[2];
00669       map[section] = nsDelim;
00670     }
00671     removeJob(it);
00672 
00673     kdDebug(5006) << "namespaces fetched" << endl;
00674     emit namespacesFetched( map );
00675   }
00676     
00677   //-----------------------------------------------------------------------------
00678   void ImapAccountBase::slotSaveNamespaces( const ImapAccountBase::nsDelimMap& map )
00679   {
00680     kdDebug(5006) << "slotSaveNamespaces " << name() << endl;
00681     // extract the needed information
00682     mNamespaces.clear();
00683     mNamespaceToDelimiter.clear();
00684     for ( uint i = 0; i < 3; ++i ) {
00685       imapNamespace section = imapNamespace( i );
00686       namespaceDelim ns = map[ section ];
00687       namespaceDelim::ConstIterator it;
00688       QStringList list;
00689       for ( it = ns.begin(); it != ns.end(); ++it ) {
00690         list += it.key();
00691         mNamespaceToDelimiter[ it.key() ] = it.data();
00692       }
00693       if ( !list.isEmpty() ) {
00694         mNamespaces[section] = list;
00695       }
00696     }
00697     // see if we need to migrate an old prefix
00698     if ( !mOldPrefix.isEmpty() ) {
00699       migratePrefix();
00700     }
00701     emit namespacesFetched();
00702   }
00703 
00704   //-----------------------------------------------------------------------------
00705   void ImapAccountBase::migratePrefix()
00706   {
00707     if ( !mOldPrefix.isEmpty() && mOldPrefix != "/" ) {
00708       // strip /
00709       if ( mOldPrefix.startsWith("/") ) {
00710         mOldPrefix = mOldPrefix.right( mOldPrefix.length()-1 );
00711       }
00712       if ( mOldPrefix.endsWith("/") ) {
00713         mOldPrefix = mOldPrefix.left( mOldPrefix.length()-1 );
00714       }
00715       QStringList list = mNamespaces[PersonalNS];
00716       bool done = false;
00717       for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00718         if ( (*it).startsWith( mOldPrefix ) ) {
00719           // should be ok
00720           done = true;
00721           kdDebug(5006) << "migratePrefix - no migration needed" << endl;
00722           break;
00723         }
00724       }
00725       if ( !done ) {
00726         QString msg = i18n("KMail has detected a prefix entry in the "
00727             "configuration of the account \"%1\" which is obsolete with the "
00728             "support of IMAP namespaces.").arg( name() );
00729         if ( list.contains( "" ) ) {
00730           // replace empty entry with the old prefix
00731           list.remove( "" );
00732           list += mOldPrefix;
00733           mNamespaces[PersonalNS] = list;
00734           if ( mNamespaceToDelimiter.contains( "" ) ) {
00735             QString delim = mNamespaceToDelimiter[""];
00736             mNamespaceToDelimiter.remove( "" );
00737             mNamespaceToDelimiter[mOldPrefix] = delim;
00738           }
00739           kdDebug(5006) << "migratePrefix - replaced empty with " << mOldPrefix << endl;
00740           msg += i18n("The configuration was automatically migrated but you should check "
00741               "your account configuration.");
00742         } else if ( list.count() == 1 ) {
00743           // only one entry in the personal namespace so replace it
00744           QString old = list.first();
00745           list.clear();
00746           list += mOldPrefix;
00747           mNamespaces[PersonalNS] = list;
00748           if ( mNamespaceToDelimiter.contains( old ) ) {
00749             QString delim = mNamespaceToDelimiter[old];
00750             mNamespaceToDelimiter.remove( old );
00751             mNamespaceToDelimiter[mOldPrefix] = delim;
00752           }
00753           kdDebug(5006) << "migratePrefix - replaced single with " << mOldPrefix << endl;
00754           msg += i18n("The configuration was automatically migrated but you should check "
00755               "your account configuration.");
00756         } else {
00757           kdDebug(5006) << "migratePrefix - migration failed" << endl;
00758           msg += i18n("It was not possible to migrate your configuration automatically "
00759               "so please check your account configuration.");
00760         }
00761         KMessageBox::information( kmkernel->getKMMainWidget(), msg );
00762       }
00763     } else 
00764     {
00765       kdDebug(5006) << "migratePrefix - no migration needed" << endl;
00766     }
00767     mOldPrefix = "";
00768   }
00769 
00770   //-----------------------------------------------------------------------------
00771   QString ImapAccountBase::namespaceForFolder( FolderStorage* storage )
00772   {
00773     QString path;
00774     if ( storage->folderType() == KMFolderTypeImap ) {
00775       path = static_cast<KMFolderImap*>( storage )->imapPath();
00776     } else if ( storage->folderType() == KMFolderTypeCachedImap ) {
00777       path = static_cast<KMFolderCachedImap*>( storage )->imapPath();
00778     }
00779 
00780     nsMap::Iterator it;
00781     for ( it = mNamespaces.begin(); it != mNamespaces.end(); ++it )
00782     {
00783       QStringList::Iterator strit;
00784       for ( strit = it.data().begin(); strit != it.data().end(); ++strit )
00785       {
00786         QString ns = *strit;
00787         if ( ns.endsWith("/") || ns.endsWith(".") ) {
00788           // strip delimiter for the comparison
00789           ns = ns.left( ns.length()-1 );
00790         }
00791         // first ignore an empty prefix as it would match always
00792         if ( !ns.isEmpty() && path.find( ns ) != -1 ) {
00793           return (*strit);
00794         }
00795       }
00796     }
00797     return QString::null;
00798   }  
00799 
00800   //-----------------------------------------------------------------------------
00801   QString ImapAccountBase::delimiterForNamespace( const QString& prefix )
00802   {
00803     kdDebug(5006) << "delimiterForNamespace " << prefix << endl;
00804     // try to match exactly
00805     if ( mNamespaceToDelimiter.contains(prefix) ) {
00806       return mNamespaceToDelimiter[prefix];
00807     }
00808 
00809     // then try if the prefix is part of a namespace
00810     // exclude empty namespace
00811     for ( namespaceDelim::ConstIterator it = mNamespaceToDelimiter.begin(); 
00812           it != mNamespaceToDelimiter.end(); ++it ) {
00813       // the namespace definition sometimes contains the delimiter
00814       // make sure we also match this version
00815       QString stripped = it.key().left( it.key().length() - 1 );
00816       if ( !it.key().isEmpty() && 
00817           ( prefix.contains( it.key() ) || prefix.contains( stripped ) ) ) {
00818         return it.data();
00819       }
00820     }
00821     // see if we have an empty namespace
00822     // this should always be the case
00823     if ( mNamespaceToDelimiter.contains( "" ) ) {
00824       return mNamespaceToDelimiter[""];
00825     }
00826     // well, we tried
00827     kdDebug(5006) << "delimiterForNamespace - not found" << endl;
00828     return QString::null;
00829   }
00830 
00831   //-----------------------------------------------------------------------------
00832   QString ImapAccountBase::delimiterForFolder( FolderStorage* storage )
00833   {
00834     QString prefix = namespaceForFolder( storage );
00835     QString delim = delimiterForNamespace( prefix );
00836     return delim;
00837   }
00838 
00839   //-----------------------------------------------------------------------------
00840   void ImapAccountBase::slotSimpleResult(KIO::Job * job)
00841   {
00842     JobIterator it = findJob( job );
00843     bool quiet = false;
00844     if (it != mapJobData.end()) {
00845       quiet = (*it).quiet;
00846       if ( !(job->error() && !quiet) ) // the error handler removes in that case
00847         removeJob(it);
00848     }
00849     if (job->error()) {
00850       if (!quiet)
00851         handleJobError(job, QString::null );
00852       else {
00853         if ( job->error() == KIO::ERR_CONNECTION_BROKEN && slave() ) {
00854           // make sure ERR_CONNECTION_BROKEN is properly handled and the slave
00855           // disconnected even when quiet()
00856           KIO::Scheduler::disconnectSlave( slave() );
00857           mSlave = 0;
00858         }
00859         if (job->error() == KIO::ERR_SLAVE_DIED)
00860           slaveDied();
00861       }
00862     }
00863   }
00864 
00865   //-----------------------------------------------------------------------------
00866   bool ImapAccountBase::handlePutError( KIO::Job* job, jobData& jd, KMFolder* folder )
00867   {
00868     Q_ASSERT( !jd.msgList.isEmpty() );
00869     KMMessage* msg = jd.msgList.first();
00870     // Use double-quotes around the subject to keep the sentence readable,
00871     // but don't use double quotes around the sender since from() might return a double-quoted name already
00872     const QString subject = msg->subject().isEmpty() ? i18n( "<unknown>" ) : QString("\"%1\"").arg( msg->subject() );
00873     const QString from = msg->from().isEmpty() ? i18n( "<unknown>" ) : msg->from();
00874     QString myError = "<p><b>" + i18n("Error while uploading message")
00875       + "</b></p><p>"
00876       + 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 ) )
00877       + "</p><p>"
00878       + i18n("The destination folder was %1, which has the URL %2.").arg( QStyleSheet::escape( folder->label() ), QStyleSheet::escape( jd.htmlURL() ) )
00879       + "</p><p>"
00880       + i18n("The error message from the server communication is here:") + "</p>";
00881     return handleJobError( job, myError );
00882   }
00883 
00884   //-----------------------------------------------------------------------------
00885   bool ImapAccountBase::handleError( int errorCode, const QString &errorMsg, KIO::Job* job, const QString& context, bool abortSync )
00886   {
00887     // Copy job's data before a possible killAllJobs
00888     QStringList errors;
00889     if ( job && job->error() != KIO::ERR_SLAVE_DEFINED /*workaround for kdelibs-3.2*/)
00890       errors = job->detailedErrorStrings();
00891 
00892     bool jobsKilled = true;
00893     switch( errorCode ) {
00894     case KIO::ERR_SLAVE_DIED: slaveDied(); killAllJobs( true ); break;
00895     case KIO::ERR_COULD_NOT_AUTHENTICATE: // bad password
00896       mAskAgain = true;
00897       // fallthrough intended
00898     case KIO::ERR_CONNECTION_BROKEN:
00899     case KIO::ERR_COULD_NOT_CONNECT:
00900     case KIO::ERR_SERVER_TIMEOUT:
00901       // These mean that we'll have to reconnect on the next attempt, so disconnect and set mSlave to 0.
00902       killAllJobs( true );
00903       break;
00904     case KIO::ERR_COULD_NOT_LOGIN:
00905     case KIO::ERR_USER_CANCELED:
00906       killAllJobs( false );
00907       break;
00908     default:
00909       if ( abortSync )
00910         killAllJobs( false );
00911       else
00912         jobsKilled = false;
00913       break;
00914     }
00915 
00916     // check if we still display an error
00917     if ( !mErrorDialogIsActive && errorCode != KIO::ERR_USER_CANCELED ) {
00918       mErrorDialogIsActive = true;
00919       QString msg = context + '\n' + KIO::buildErrorString( errorCode, errorMsg );
00920       QString caption = i18n("Error");
00921       
00922       if ( jobsKilled || errorCode == KIO::ERR_COULD_NOT_LOGIN ) {
00923         if ( errorCode == KIO::ERR_SERVER_TIMEOUT || errorCode == KIO::ERR_CONNECTION_BROKEN ) {
00924           msg = i18n("The connection to the server %1 was unexpectedly closed or timed out. It will be re-established automatically if possible.").
00925             arg( name() );
00926           KMessageBox::information( kapp->activeWindow(), msg, caption, "kmailConnectionBrokenErrorDialog" );
00927           // Show it in the status bar, in case the user has ticked "don't show again"
00928           if ( errorCode == KIO::ERR_CONNECTION_BROKEN )
00929             KPIM::BroadcastStatus::instance()->setStatusMsg(
00930                 i18n(  "The connection to account %1 was broken." ).arg( name() ) );
00931           else if ( errorCode == KIO::ERR_SERVER_TIMEOUT )
00932             KPIM::BroadcastStatus::instance()->setStatusMsg(
00933                 i18n(  "The connection to account %1 timed out." ).arg( name() ) );
00934         } else {
00935           if ( !errors.isEmpty() )
00936               KMessageBox::detailedError( kapp->activeWindow(), msg, errors.join("\n").prepend("<qt>"), caption );
00937           else
00938               KMessageBox::error( kapp->activeWindow(), msg, caption );
00939           }
00940       }
00941       else { // i.e. we have a chance to continue, ask the user about it
00942         if ( errors.count() >= 3 ) { // there is no detailedWarningContinueCancel... (#86517)
00943           msg = QString( "<qt>") + context + errors[1] + '\n' + errors[2];
00944           caption = errors[0];
00945         }
00946         int ret = KMessageBox::warningContinueCancel( kapp->activeWindow(), msg, caption );
00947         if ( ret == KMessageBox::Cancel ) {
00948           jobsKilled = true;
00949           killAllJobs( false );
00950         }
00951       }
00952       mErrorDialogIsActive = false;
00953     } else {
00954       if ( mErrorDialogIsActive )
00955         kdDebug(5006) << "suppressing error:" << errorMsg << endl;
00956     }
00957     if ( job && !jobsKilled )
00958       removeJob( job );
00959     return !jobsKilled; // jobsKilled==false -> continue==true
00960     }
00961 
00962   //-----------------------------------------------------------------------------
00963   void ImapAccountBase::cancelMailCheck()
00964   {
00965     QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00966     while ( it != mapJobData.end() ) {
00967       kdDebug(5006) << "cancelMailCheck: job is cancellable: " << (*it).cancellable << endl;
00968       if ( (*it).cancellable ) {
00969         it.key()->kill();
00970         QMap<KIO::Job*, jobData>::Iterator rmit = it;
00971         ++it;
00972         mapJobData.remove( rmit );
00973         // We killed a job -> this kills the slave
00974         mSlave = 0;
00975       } else
00976         ++it;
00977     }
00978 
00979     for( QPtrListIterator<FolderJob> it( mJobList ); it.current(); ++it ) {
00980       if ( it.current()->isCancellable() ) {
00981         FolderJob* job = it.current();
00982         job->setPassiveDestructor( true );
00983         mJobList.remove( job );
00984         delete job;
00985       } else
00986         ++it;
00987     }
00988   }
00989 
00990 
00991   //-----------------------------------------------------------------------------
00992   QString ImapAccountBase::jobData::htmlURL() const
00993   {
00994     KURL u(  url );
00995     return u.htmlURL();
00996   }
00997 
00998   //-----------------------------------------------------------------------------
00999   void ImapAccountBase::processNewMailSingleFolder(KMFolder* folder)
01000   {
01001     mFoldersQueuedForChecking.append(folder);
01002     mCheckingSingleFolder = true;
01003     if ( checkingMail() )
01004     {
01005       disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01006                   this, SLOT( slotCheckQueuedFolders() ) );
01007       connect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01008                this, SLOT( slotCheckQueuedFolders() ) );
01009     } else {
01010       slotCheckQueuedFolders();
01011     }
01012   }
01013 
01014   //-----------------------------------------------------------------------------
01015   void ImapAccountBase::slotCheckQueuedFolders()
01016   {
01017     disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01018                 this, SLOT( slotCheckQueuedFolders() ) );
01019 
01020     QValueList<QGuardedPtr<KMFolder> > mSaveList = mMailCheckFolders;
01021     mMailCheckFolders = mFoldersQueuedForChecking;
01022     if ( kmkernel->acctMgr() )
01023       kmkernel->acctMgr()->singleCheckMail(this, true);
01024     mMailCheckFolders = mSaveList;
01025     mFoldersQueuedForChecking.clear();
01026   }
01027 
01028   //-----------------------------------------------------------------------------
01029   bool ImapAccountBase::checkingMail( KMFolder *folder )
01030   {
01031     if (checkingMail() && mFoldersQueuedForChecking.contains(folder))
01032       return true;
01033     return false;
01034   }
01035 
01036   //-----------------------------------------------------------------------------
01037   void ImapAccountBase::handleBodyStructure( QDataStream & stream, KMMessage * msg,
01038                                              const AttachmentStrategy *as )
01039   {
01040     mBodyPartList.clear();
01041     mCurrentMsg = msg;
01042     // first delete old parts as we construct our own
01043     msg->deleteBodyParts();
01044     // make the parts and fill the mBodyPartList
01045     constructParts( stream, 1, 0, 0, msg->asDwMessage() );
01046     if ( mBodyPartList.count() == 1 ) // we directly set the body later, at partsToLoad below
01047       msg->deleteBodyParts();
01048 
01049     if ( !as )
01050     {
01051       kdWarning(5006) << k_funcinfo << " - found no attachment strategy!" << endl;
01052       return;
01053     }
01054 
01055     // see what parts have to loaded according to attachmentstrategy
01056     BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as );
01057     visitor->visit( mBodyPartList );
01058     QPtrList<KMMessagePart> parts = visitor->partsToLoad();
01059     delete visitor;
01060     QPtrListIterator<KMMessagePart> it( parts );
01061     KMMessagePart *part;
01062     int partsToLoad = 0;
01063     // check how many parts we have to load
01064     while ( (part = it.current()) != 0 )
01065     {
01066       ++it;
01067       if ( part->loadPart() )
01068       {
01069         ++partsToLoad;
01070       }
01071     }
01072     // if the only body part is not text, part->loadPart() would return false
01073     // and that part is never loaded, so make sure it loads.
01074     // it seems that TEXT does load the single body part even if it is not text/*
01075     if ( mBodyPartList.count() == 1 && partsToLoad == 0 )
01076         partsToLoad = 1; // this causes the next test to succeed, and loads the whole message
01077 
01078     if ( (mBodyPartList.count() * 0.5) < partsToLoad )
01079     {
01080       // more than 50% of the parts have to be loaded anyway so it is faster
01081       // to load the message completely
01082       kdDebug(5006) << "Falling back to normal mode" << endl;
01083       FolderJob *job = msg->parent()->createJob(
01084           msg, FolderJob::tGetMessage, 0, "TEXT" );
01085       job->start();
01086       return;
01087     }
01088     it.toFirst();
01089     while ( (part = it.current()) != 0 )
01090     {
01091       ++it;
01092       kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier()
01093         << " (" << part->originalContentTypeStr() << ")" << endl;
01094       if ( part->loadHeaders() )
01095       {
01096         kdDebug(5006) << "load HEADER" << endl;
01097         FolderJob *job = msg->parent()->createJob(
01098             msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" );
01099         job->start();
01100       }
01101       if ( part->loadPart() )
01102       {
01103         kdDebug(5006) << "load Part" << endl;
01104         FolderJob *job = msg->parent()->createJob(
01105             msg, FolderJob::tGetMessage, 0, part->partSpecifier() );
01106         job->start();
01107       }
01108     }
01109   }
01110 
01111   //-----------------------------------------------------------------------------
01112   void ImapAccountBase::constructParts( QDataStream & stream, int count, KMMessagePart* parentKMPart,
01113                                         DwBodyPart * parent, const DwMessage * dwmsg )
01114   {
01115     int children;
01116     for (int i = 0; i < count; i++)
01117     {
01118       stream >> children;
01119       KMMessagePart* part = new KMMessagePart( stream );
01120       part->setParent( parentKMPart );
01121       mBodyPartList.append( part );
01122       kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier()
01123         << " of type " << part->originalContentTypeStr() << endl;
01124       DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part );
01125 
01126       if ( parent )
01127       {
01128         // add to parent body
01129         parent->Body().AddBodyPart( dwpart );
01130         dwpart->Parse();
01131 //        kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
01132 //          << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
01133       } else if ( part->partSpecifier() != "0" &&
01134                   !part->partSpecifier().endsWith(".HEADER") )
01135       {
01136         // add to message
01137         dwmsg->Body().AddBodyPart( dwpart );
01138         dwpart->Parse();
01139 //        kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
01140 //          << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
01141       } else
01142         dwpart = 0;
01143 
01144       if ( !parentKMPart )
01145         parentKMPart = part;
01146 
01147       if (children > 0)
01148       {
01149         DwBodyPart* newparent = dwpart;
01150         const DwMessage* newmsg = dwmsg;
01151         if ( part->originalContentTypeStr() == "MESSAGE/RFC822" && dwpart &&
01152              dwpart->Body().Message() )
01153         {
01154           // set the encapsulated message as the new message
01155           newparent = 0;
01156           newmsg = dwpart->Body().Message();
01157         }
01158         KMMessagePart* newParentKMPart = part;
01159         if ( part->partSpecifier().endsWith(".HEADER") ) // we don't want headers as parent
01160           newParentKMPart = parentKMPart;
01161 
01162         constructParts( stream, children, newParentKMPart, newparent, newmsg );
01163       }
01164     }
01165   }
01166 
01167   //-----------------------------------------------------------------------------
01168   void ImapAccountBase::setImapStatus( KMFolder* folder, const QString& path, const QCString& flags )
01169   {
01170      // set the status on the server, the uids are integrated in the path
01171      kdDebug(5006) << "setImapStatus path=" << path << " to: " << flags << endl;
01172      KURL url = getUrl();
01173      url.setPath(path);
01174 
01175      QByteArray packedArgs;
01176      QDataStream stream( packedArgs, IO_WriteOnly);
01177 
01178      stream << (int) 'S' << url << flags;
01179 
01180      if ( makeConnection() != Connected ) 
01181        return; // can't happen with dimap
01182 
01183      KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
01184      KIO::Scheduler::assignJobToSlave(slave(), job);
01185      ImapAccountBase::jobData jd( url.url(), folder );
01186      jd.path = path;
01187      insertJob(job, jd);
01188      connect(job, SIGNAL(result(KIO::Job *)),
01189            SLOT(slotSetStatusResult(KIO::Job *)));
01190   }
01191   //-----------------------------------------------------------------------------
01192   void ImapAccountBase::slotSetStatusResult(KIO::Job * job)
01193   {
01194      ImapAccountBase::JobIterator it = findJob(job);
01195      if ( it == jobsEnd() ) return;
01196      int errorCode = job->error();
01197      KMFolder * const parent = (*it).parent;
01198      const QString path = (*it).path;
01199      if (errorCode && errorCode != KIO::ERR_CANNOT_OPEN_FOR_WRITING)
01200      {
01201        bool cont = handleJobError( job, i18n( "Error while uploading status of messages to server: " ) + '\n' );
01202        emit imapStatusChanged( parent, path, cont );
01203      }
01204      else
01205      {
01206        emit imapStatusChanged( parent, path, true );
01207        removeJob(it);
01208      }
01209   }
01210 
01211   //-----------------------------------------------------------------------------
01212   void ImapAccountBase::setFolder(KMFolder* folder, bool addAccount)
01213   {
01214     if (folder)
01215     {
01216       folder->setSystemLabel(name());
01217       folder->setId(id());
01218     }
01219     NetworkAccount::setFolder(folder, addAccount);
01220   }
01221 
01222   //-----------------------------------------------------------------------------
01223   void ImapAccountBase::removeJob( JobIterator& it )
01224   {
01225     if( (*it).progressItem ) {
01226       (*it).progressItem->setComplete();
01227       (*it).progressItem = 0;
01228     }
01229     mapJobData.remove( it );
01230   }
01231 
01232   //-----------------------------------------------------------------------------
01233   void KMail::ImapAccountBase::removeJob( KIO::Job* job )
01234   {
01235     mapJobData.remove( job );
01236   }
01237 
01238   //-----------------------------------------------------------------------------
01239   KPIM::ProgressItem* ImapAccountBase::listDirProgressItem()
01240   {
01241     if ( !mListDirProgressItem )
01242     {
01243       mListDirProgressItem = ProgressManager::createProgressItem(
01244           "ListDir" + name(),
01245           QStyleSheet::escape( name() ),
01246           i18n("retrieving folders"),
01247           true,
01248           useSSL() || useTLS() );
01249       connect ( mListDirProgressItem,
01250                 SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
01251                 this,
01252                 SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
01253       // Start with a guessed value of the old folder count plus 5%. As long
01254       // as the list of folders doesn't constantly change, that should be good
01255       // enough.
01256       unsigned int count = folderCount();
01257       mListDirProgressItem->setTotalItems( count + (unsigned int)(count*0.05) );
01258     }
01259     return mListDirProgressItem;
01260   }
01261 
01262   //-----------------------------------------------------------------------------
01263   unsigned int ImapAccountBase::folderCount() const
01264   {
01265     if ( !rootFolder() || !rootFolder()->folder() || !rootFolder()->folder()->child() )
01266       return 0;
01267     return kmkernel->imapFolderMgr()->folderCount( rootFolder()->folder()->child() );
01268   }
01269 
01270   //------------------------------------------------------------------------------
01271   QString ImapAccountBase::addPathToNamespace( const QString& prefix )
01272   {
01273     QString myPrefix = prefix;
01274     if ( !myPrefix.startsWith( "/" ) ) {
01275       myPrefix = "/" + myPrefix;
01276     }
01277     if ( !myPrefix.endsWith( "/" ) ) {
01278       myPrefix += "/";
01279     }
01280 
01281     return myPrefix;
01282   }
01283 
01284   //------------------------------------------------------------------------------
01285   bool ImapAccountBase::isNamespaceFolder( QString& name )
01286   {
01287     QStringList ns = mNamespaces[OtherUsersNS];
01288     ns += mNamespaces[SharedNS];
01289     ns += mNamespaces[PersonalNS];
01290     QString nameWithDelimiter;
01291     for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
01292     {
01293       nameWithDelimiter = name + delimiterForNamespace( *it );
01294       if ( *it == name || *it == nameWithDelimiter )
01295         return true;
01296     }
01297     return false;
01298   }
01299 
01300   //------------------------------------------------------------------------------
01301   ImapAccountBase::nsDelimMap ImapAccountBase::namespacesWithDelimiter()
01302   {
01303     nsDelimMap map;
01304     nsMap::ConstIterator it;
01305     for ( uint i = 0; i < 3; ++i )
01306     {
01307       imapNamespace section = imapNamespace( i );
01308       QStringList namespaces = mNamespaces[section];
01309       namespaceDelim nsDelim;
01310       QStringList::Iterator lit;
01311       for ( lit = namespaces.begin(); lit != namespaces.end(); ++lit )
01312       {
01313         nsDelim[*lit] = delimiterForNamespace( *lit );
01314       }
01315       map[section] = nsDelim;
01316     }
01317     return map;
01318   }
01319 
01320   //------------------------------------------------------------------------------
01321   QString ImapAccountBase::createImapPath( const QString& parent, 
01322                                            const QString& folderName )
01323   {
01324     kdDebug(5006) << "createImapPath parent="<<parent<<", folderName="<<folderName<<endl;  
01325     QString newName = parent;
01326     // strip / at the end
01327     if ( newName.endsWith("/") ) {
01328       newName = newName.left( newName.length() - 1 );
01329     }
01330     // add correct delimiter
01331     QString delim = delimiterForNamespace( newName );
01332     // should not happen...
01333     if ( delim.isEmpty() ) {
01334       delim = "/";
01335     }
01336     if ( !newName.isEmpty() && 
01337          !newName.endsWith( delim ) && !folderName.startsWith( delim ) ) {
01338       newName = newName + delim;
01339     }
01340     newName = newName + folderName;
01341     // add / at the end
01342     if ( !newName.endsWith("/") ) {
01343       newName = newName + "/";
01344     }
01345 
01346     return newName;
01347   }
01348 
01349   //------------------------------------------------------------------------------
01350   QString ImapAccountBase::createImapPath( FolderStorage* parent, 
01351                                            const QString& folderName )
01352   {
01353     QString path;
01354     if ( parent->folderType() == KMFolderTypeImap ) {
01355       path = static_cast<KMFolderImap*>( parent )->imapPath();
01356     } else if ( parent->folderType() == KMFolderTypeCachedImap ) {
01357       path = static_cast<KMFolderCachedImap*>( parent )->imapPath();
01358     } else {
01359       // error
01360       return path;
01361     }
01362     
01363     return createImapPath( path, folderName );
01364   }
01365 
01366 
01367   bool ImapAccountBase::locallySubscribedTo( const QString& imapPath )
01368   {
01369       return mLocalSubscriptionBlackList.find( imapPath ) == mLocalSubscriptionBlackList.end();
01370   }
01371 
01372   void ImapAccountBase::changeLocalSubscription( const QString& imapPath, bool subscribe )
01373   {
01374     if ( subscribe ) {
01375       // find in blacklist and remove from it
01376       mLocalSubscriptionBlackList.erase( imapPath );
01377     } else {
01378       // blacklist
01379       mLocalSubscriptionBlackList.insert( imapPath );
01380     }
01381   }
01382 
01383 
01384   QStringList ImapAccountBase::locallyBlacklistedFolders() const
01385   {
01386       QStringList list;
01387       std::set<QString>::const_iterator it = mLocalSubscriptionBlackList.begin();
01388       std::set<QString>::const_iterator end = mLocalSubscriptionBlackList.end();
01389       for ( ; it != end ; ++it )
01390         list.append( *it );
01391       return list;
01392   }
01393 
01394   void ImapAccountBase::localBlacklistFromStringList( const QStringList &list )
01395   {
01396     for( QStringList::ConstIterator it = list.constBegin( ); it != list.constEnd( ); ++it )
01397       mLocalSubscriptionBlackList.insert( *it );
01398   }
01399 
01400 } // namespace KMail
01401 
01402 #include "imapaccountbase.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys