kmail

imapaccountbase.cpp

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