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       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 %2 with subject %3 on the server.").arg( msg->dateStr(), QStyleSheet::escape( from ), QStyleSheet::escape( subject ) )
00879       + "</p><p>"
00880       + i18n("The destination folder was %1, which has the URL %2.").arg( QStyleSheet::escape( folder->label() ), QStyleSheet::escape( jd.htmlURL() ) )
00881       + "</p><p>"
00882       + i18n("The error message from the server communication is here:") + "</p>";
00883     return handleJobError( job, myError );
00884   }
00885 
00886   //-----------------------------------------------------------------------------
00887   bool ImapAccountBase::handleError( int errorCode, const QString &errorMsg, KIO::Job* job, const QString& context, bool abortSync )
00888   {
00889     // Copy job's data before a possible killAllJobs
00890     QStringList errors;
00891     if ( job && job->error() != KIO::ERR_SLAVE_DEFINED /*workaround for kdelibs-3.2*/)
00892       errors = job->detailedErrorStrings();
00893 
00894     bool jobsKilled = true;
00895     switch( errorCode ) {
00896     case KIO::ERR_SLAVE_DIED: slaveDied(); killAllJobs( true ); break;
00897     case KIO::ERR_COULD_NOT_AUTHENTICATE: // bad password
00898       mAskAgain = true;
00899       // fallthrough intended
00900     case KIO::ERR_CONNECTION_BROKEN:
00901     case KIO::ERR_COULD_NOT_CONNECT:
00902     case KIO::ERR_SERVER_TIMEOUT:
00903       // These mean that we'll have to reconnect on the next attempt, so disconnect and set mSlave to 0.
00904       killAllJobs( true );
00905       break;
00906     case KIO::ERR_COULD_NOT_LOGIN:
00907     case KIO::ERR_USER_CANCELED:
00908       killAllJobs( false );
00909       break;
00910     default:
00911       if ( abortSync )
00912         killAllJobs( false );
00913       else
00914         jobsKilled = false;
00915       break;
00916     }
00917 
00918     // check if we still display an error
00919     if ( !mErrorDialogIsActive && errorCode != KIO::ERR_USER_CANCELED ) {
00920       mErrorDialogIsActive = true;
00921       QString msg = context + '\n' + KIO::buildErrorString( errorCode, errorMsg );
00922       QString caption = i18n("Error");
00923       
00924       if ( jobsKilled || errorCode == KIO::ERR_COULD_NOT_LOGIN ) {
00925         if ( errorCode == KIO::ERR_SERVER_TIMEOUT || errorCode == KIO::ERR_CONNECTION_BROKEN ) {
00926           msg = i18n("The connection to the server %1 was unexpectedly closed or timed out. It will be re-established automatically if possible.").
00927             arg( name() );
00928           KMessageBox::information( kapp->activeWindow(), msg, caption, "kmailConnectionBrokenErrorDialog" );
00929           // Show it in the status bar, in case the user has ticked "don't show again"
00930           if ( errorCode == KIO::ERR_CONNECTION_BROKEN )
00931             KPIM::BroadcastStatus::instance()->setStatusMsg(
00932                 i18n(  "The connection to account %1 was broken." ).arg( name() ) );
00933           else if ( errorCode == KIO::ERR_SERVER_TIMEOUT )
00934             KPIM::BroadcastStatus::instance()->setStatusMsg(
00935                 i18n(  "The connection to account %1 timed out." ).arg( name() ) );
00936         } else {
00937           if ( !errors.isEmpty() )
00938               KMessageBox::detailedError( kapp->activeWindow(), msg, errors.join("\n").prepend("<qt>"), caption );
00939           else
00940               KMessageBox::error( kapp->activeWindow(), msg, caption );
00941           }
00942       }
00943       else { // i.e. we have a chance to continue, ask the user about it
00944         if ( errors.count() >= 3 ) { // there is no detailedWarningContinueCancel... (#86517)
00945           msg = QString( "<qt>") + context + errors[1] + '\n' + errors[2];
00946           caption = errors[0];
00947         }
00948         int ret = KMessageBox::warningContinueCancel( kapp->activeWindow(), msg, caption );
00949         if ( ret == KMessageBox::Cancel ) {
00950           jobsKilled = true;
00951           killAllJobs( false );
00952         }
00953       }
00954       mErrorDialogIsActive = false;
00955     } else {
00956       if ( mErrorDialogIsActive )
00957         kdDebug(5006) << "suppressing error:" << errorMsg << endl;
00958     }
00959     if ( job && !jobsKilled )
00960       removeJob( job );
00961     return !jobsKilled; // jobsKilled==false -> continue==true
00962     }
00963 
00964   //-----------------------------------------------------------------------------
00965   void ImapAccountBase::cancelMailCheck()
00966   {
00967     QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00968     while ( it != mapJobData.end() ) {
00969       kdDebug(5006) << "cancelMailCheck: job is cancellable: " << (*it).cancellable << endl;
00970       if ( (*it).cancellable ) {
00971         it.key()->kill();
00972         QMap<KIO::Job*, jobData>::Iterator rmit = it;
00973         ++it;
00974         mapJobData.remove( rmit );
00975         // We killed a job -> this kills the slave
00976         mSlave = 0;
00977       } else
00978         ++it;
00979     }
00980 
00981     for( QPtrListIterator<FolderJob> it( mJobList ); it.current(); ++it ) {
00982       if ( it.current()->isCancellable() ) {
00983         FolderJob* job = it.current();
00984         job->setPassiveDestructor( true );
00985         mJobList.remove( job );
00986         delete job;
00987       } else
00988         ++it;
00989     }
00990   }
00991 
00992 
00993   //-----------------------------------------------------------------------------
00994   QString ImapAccountBase::jobData::htmlURL() const
00995   {
00996     KURL u(  url );
00997     return u.htmlURL();
00998   }
00999 
01000   //-----------------------------------------------------------------------------
01001   void ImapAccountBase::processNewMailSingleFolder(KMFolder* folder)
01002   {
01003     mFoldersQueuedForChecking.append(folder);
01004     mCheckingSingleFolder = true;
01005     if ( checkingMail() )
01006     {
01007       disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01008                   this, SLOT( slotCheckQueuedFolders() ) );
01009       connect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01010                this, SLOT( slotCheckQueuedFolders() ) );
01011     } else {
01012       slotCheckQueuedFolders();
01013     }
01014   }
01015 
01016   //-----------------------------------------------------------------------------
01017   void ImapAccountBase::slotCheckQueuedFolders()
01018   {
01019     disconnect( this, SIGNAL( finishedCheck( bool, CheckStatus ) ),
01020                 this, SLOT( slotCheckQueuedFolders() ) );
01021 
01022     QValueList<QGuardedPtr<KMFolder> > mSaveList = mMailCheckFolders;
01023     mMailCheckFolders = mFoldersQueuedForChecking;
01024     if ( kmkernel->acctMgr() )
01025       kmkernel->acctMgr()->singleCheckMail(this, true);
01026     mMailCheckFolders = mSaveList;
01027     mFoldersQueuedForChecking.clear();
01028   }
01029 
01030   //-----------------------------------------------------------------------------
01031   bool ImapAccountBase::checkingMail( KMFolder *folder )
01032   {
01033     if (checkingMail() && mFoldersQueuedForChecking.contains(folder))
01034       return true;
01035     return false;
01036   }
01037 
01038   //-----------------------------------------------------------------------------
01039   void ImapAccountBase::handleBodyStructure( QDataStream & stream, KMMessage * msg,
01040                                              const AttachmentStrategy *as )
01041   {
01042     mBodyPartList.clear();
01043     mCurrentMsg = msg;
01044     // first delete old parts as we construct our own
01045     msg->deleteBodyParts();
01046     // make the parts and fill the mBodyPartList
01047     constructParts( stream, 1, 0, 0, msg->asDwMessage() );
01048     if ( mBodyPartList.count() == 1 ) // we directly set the body later, at partsToLoad below
01049       msg->deleteBodyParts();
01050 
01051     if ( !as )
01052     {
01053       kdWarning(5006) << k_funcinfo << " - found no attachment strategy!" << endl;
01054       return;
01055     }
01056 
01057     // see what parts have to loaded according to attachmentstrategy
01058     BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as );
01059     visitor->visit( mBodyPartList );
01060     QPtrList<KMMessagePart> parts = visitor->partsToLoad();
01061     delete visitor;
01062     QPtrListIterator<KMMessagePart> it( parts );
01063     KMMessagePart *part;
01064     int partsToLoad = 0;
01065     // check how many parts we have to load
01066     while ( (part = it.current()) != 0 )
01067     {
01068       ++it;
01069       if ( part->loadPart() )
01070       {
01071         ++partsToLoad;
01072       }
01073     }
01074     // if the only body part is not text, part->loadPart() would return false
01075     // and that part is never loaded, so make sure it loads.
01076     // it seems that TEXT does load the single body part even if it is not text/*
01077     if ( mBodyPartList.count() == 1 && partsToLoad == 0 )
01078         partsToLoad = 1; // this causes the next test to succeed, and loads the whole message
01079 
01080     if ( (mBodyPartList.count() * 0.5) < partsToLoad )
01081     {
01082       // more than 50% of the parts have to be loaded anyway so it is faster
01083       // to load the message completely
01084       kdDebug(5006) << "Falling back to normal mode" << endl;
01085       FolderJob *job = msg->parent()->createJob(
01086           msg, FolderJob::tGetMessage, 0, "TEXT" );
01087       job->start();
01088       return;
01089     }
01090     it.toFirst();
01091     while ( (part = it.current()) != 0 )
01092     {
01093       ++it;
01094       kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier()
01095         << " (" << part->originalContentTypeStr() << ")" << endl;
01096       if ( part->loadHeaders() )
01097       {
01098         kdDebug(5006) << "load HEADER" << endl;
01099         FolderJob *job = msg->parent()->createJob(
01100             msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" );
01101         job->start();
01102       }
01103       if ( part->loadPart() )
01104       {
01105         kdDebug(5006) << "load Part" << endl;
01106         FolderJob *job = msg->parent()->createJob(
01107             msg, FolderJob::tGetMessage, 0, part->partSpecifier() );
01108         job->start();
01109       }
01110     }
01111   }
01112 
01113   //-----------------------------------------------------------------------------
01114   void ImapAccountBase::constructParts( QDataStream & stream, int count, KMMessagePart* parentKMPart,
01115                                         DwBodyPart * parent, const DwMessage * dwmsg )
01116   {
01117     int children;
01118     for (int i = 0; i < count; i++)
01119     {
01120       stream >> children;
01121       KMMessagePart* part = new KMMessagePart( stream );
01122       part->setParent( parentKMPart );
01123       mBodyPartList.append( part );
01124       kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier()
01125         << " of type " << part->originalContentTypeStr() << endl;
01126       DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part );
01127 
01128       if ( parent )
01129       {
01130         // add to parent body
01131         parent->Body().AddBodyPart( dwpart );
01132         dwpart->Parse();
01133 //        kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
01134 //          << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
01135       } else if ( part->partSpecifier() != "0" &&
01136                   !part->partSpecifier().endsWith(".HEADER") )
01137       {
01138         // add to message
01139         dwmsg->Body().AddBodyPart( dwpart );
01140         dwpart->Parse();
01141 //        kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
01142 //          << ",dwparts msg " << dwpart->Body().Message() <<",id "<<dwpart->ObjectId() << endl;
01143       } else
01144         dwpart = 0;
01145 
01146       if ( !parentKMPart )
01147         parentKMPart = part;
01148 
01149       if (children > 0)
01150       {
01151         DwBodyPart* newparent = dwpart;
01152         const DwMessage* newmsg = dwmsg;
01153         if ( part->originalContentTypeStr() == "MESSAGE/RFC822" && dwpart &&
01154              dwpart->Body().Message() )
01155         {
01156           // set the encapsulated message as the new message
01157           newparent = 0;
01158           newmsg = dwpart->Body().Message();
01159         }
01160         KMMessagePart* newParentKMPart = part;
01161         if ( part->partSpecifier().endsWith(".HEADER") ) // we don't want headers as parent
01162           newParentKMPart = parentKMPart;
01163 
01164         constructParts( stream, children, newParentKMPart, newparent, newmsg );
01165       }
01166     }
01167   }
01168 
01169   //-----------------------------------------------------------------------------
01170   void ImapAccountBase::setImapStatus( KMFolder* folder, const QString& path, const QCString& flags )
01171   {
01172      // set the status on the server, the uids are integrated in the path
01173      kdDebug(5006) << "setImapStatus path=" << path << " to: " << flags << endl;
01174      KURL url = getUrl();
01175      url.setPath(path);
01176 
01177      QByteArray packedArgs;
01178      QDataStream stream( packedArgs, IO_WriteOnly);
01179 
01180      stream << (int) 'S' << url << flags;
01181 
01182      if ( makeConnection() != Connected ) 
01183        return; // can't happen with dimap
01184 
01185      KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
01186      KIO::Scheduler::assignJobToSlave(slave(), job);
01187      ImapAccountBase::jobData jd( url.url(), folder );
01188      jd.path = path;
01189      insertJob(job, jd);
01190      connect(job, SIGNAL(result(KIO::Job *)),
01191            SLOT(slotSetStatusResult(KIO::Job *)));
01192   }
01193 
01194   void ImapAccountBase::setImapSeenStatus(KMFolder * folder, const QString & path, bool seen)
01195   {
01196      KURL url = getUrl();
01197      url.setPath(path);
01198 
01199      QByteArray packedArgs;
01200      QDataStream stream( packedArgs, IO_WriteOnly);
01201 
01202      stream << (int) 's' << url << seen;
01203 
01204      if ( makeConnection() != Connected )
01205        return; // can't happen with dimap
01206 
01207      KIO::SimpleJob *job = KIO::special(url, packedArgs, false);
01208      KIO::Scheduler::assignJobToSlave(slave(), job);
01209      ImapAccountBase::jobData jd( url.url(), folder );
01210      jd.path = path;
01211      insertJob(job, jd);
01212      connect(job, SIGNAL(result(KIO::Job *)),
01213            SLOT(slotSetStatusResult(KIO::Job *)));
01214   }
01215 
01216   //-----------------------------------------------------------------------------
01217   void ImapAccountBase::slotSetStatusResult(KIO::Job * job)
01218   {
01219      ImapAccountBase::JobIterator it = findJob(job);
01220      if ( it == jobsEnd() ) return;
01221      int errorCode = job->error();
01222      KMFolder * const parent = (*it).parent;
01223      const QString path = (*it).path;
01224      if (errorCode && errorCode != KIO::ERR_CANNOT_OPEN_FOR_WRITING)
01225      {
01226        bool cont = handleJobError( job, i18n( "Error while uploading status of messages to server: " ) + '\n' );
01227        emit imapStatusChanged( parent, path, cont );
01228      }
01229      else
01230      {
01231        emit imapStatusChanged( parent, path, true );
01232        removeJob(it);
01233      }
01234   }
01235 
01236   //-----------------------------------------------------------------------------
01237   void ImapAccountBase::setFolder(KMFolder* folder, bool addAccount)
01238   {
01239     if (folder)
01240     {
01241       folder->setSystemLabel(name());
01242       folder->setId(id());
01243     }
01244     NetworkAccount::setFolder(folder, addAccount);
01245   }
01246 
01247   //-----------------------------------------------------------------------------
01248   void ImapAccountBase::removeJob( JobIterator& it )
01249   {
01250     if( (*it).progressItem ) {
01251       (*it).progressItem->setComplete();
01252       (*it).progressItem = 0;
01253     }
01254     mapJobData.remove( it );
01255   }
01256 
01257   //-----------------------------------------------------------------------------
01258   void KMail::ImapAccountBase::removeJob( KIO::Job* job )
01259   {
01260     mapJobData.remove( job );
01261   }
01262 
01263   //-----------------------------------------------------------------------------
01264   KPIM::ProgressItem* ImapAccountBase::listDirProgressItem()
01265   {
01266     if ( !mListDirProgressItem )
01267     {
01268       mListDirProgressItem = ProgressManager::createProgressItem(
01269           "ListDir" + name(),
01270           QStyleSheet::escape( name() ),
01271           i18n("retrieving folders"),
01272           true,
01273           useSSL() || useTLS() );
01274       connect ( mListDirProgressItem,
01275                 SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
01276                 this,
01277                 SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
01278       // Start with a guessed value of the old folder count plus 5%. As long
01279       // as the list of folders doesn't constantly change, that should be good
01280       // enough.
01281       unsigned int count = folderCount();
01282       mListDirProgressItem->setTotalItems( count + (unsigned int)(count*0.05) );
01283     }
01284     return mListDirProgressItem;
01285   }
01286 
01287   //-----------------------------------------------------------------------------
01288   unsigned int ImapAccountBase::folderCount() const
01289   {
01290     if ( !rootFolder() || !rootFolder()->folder() || !rootFolder()->folder()->child() )
01291       return 0;
01292     return kmkernel->imapFolderMgr()->folderCount( rootFolder()->folder()->child() );
01293   }
01294 
01295   //------------------------------------------------------------------------------
01296   QString ImapAccountBase::addPathToNamespace( const QString& prefix )
01297   {
01298     QString myPrefix = prefix;
01299     if ( !myPrefix.startsWith( "/" ) ) {
01300       myPrefix = "/" + myPrefix;
01301     }
01302     if ( !myPrefix.endsWith( "/" ) ) {
01303       myPrefix += "/";
01304     }
01305 
01306     return myPrefix;
01307   }
01308 
01309   //------------------------------------------------------------------------------
01310   bool ImapAccountBase::isNamespaceFolder( QString& name )
01311   {
01312     QStringList ns = mNamespaces[OtherUsersNS];
01313     ns += mNamespaces[SharedNS];
01314     ns += mNamespaces[PersonalNS];
01315     QString nameWithDelimiter;
01316     for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
01317     {
01318       nameWithDelimiter = name + delimiterForNamespace( *it );
01319       if ( *it == name || *it == nameWithDelimiter )
01320         return true;
01321     }
01322     return false;
01323   }
01324 
01325   //------------------------------------------------------------------------------
01326   ImapAccountBase::nsDelimMap ImapAccountBase::namespacesWithDelimiter()
01327   {
01328     nsDelimMap map;
01329     nsMap::ConstIterator it;
01330     for ( uint i = 0; i < 3; ++i )
01331     {
01332       imapNamespace section = imapNamespace( i );
01333       QStringList namespaces = mNamespaces[section];
01334       namespaceDelim nsDelim;
01335       QStringList::Iterator lit;
01336       for ( lit = namespaces.begin(); lit != namespaces.end(); ++lit )
01337       {
01338         nsDelim[*lit] = delimiterForNamespace( *lit );
01339       }
01340       map[section] = nsDelim;
01341     }
01342     return map;
01343   }
01344 
01345   //------------------------------------------------------------------------------
01346   QString ImapAccountBase::createImapPath( const QString& parent, 
01347                                            const QString& folderName )
01348   {
01349     kdDebug(5006) << "createImapPath parent="<<parent<<", folderName="<<folderName<<endl;  
01350     QString newName = parent;
01351     // strip / at the end
01352     if ( newName.endsWith("/") ) {
01353       newName = newName.left( newName.length() - 1 );
01354     }
01355     // add correct delimiter
01356     QString delim = delimiterForNamespace( newName );
01357     // should not happen...
01358     if ( delim.isEmpty() ) {
01359       delim = "/";
01360     }
01361     if ( !newName.isEmpty() && 
01362          !newName.endsWith( delim ) && !folderName.startsWith( delim ) ) {
01363       newName = newName + delim;
01364     }
01365     newName = newName + folderName;
01366     // add / at the end
01367     if ( !newName.endsWith("/") ) {
01368       newName = newName + "/";
01369     }
01370 
01371     return newName;
01372   }
01373 
01374   //------------------------------------------------------------------------------
01375   QString ImapAccountBase::createImapPath( FolderStorage* parent, 
01376                                            const QString& folderName )
01377   {
01378     QString path;
01379     if ( parent->folderType() == KMFolderTypeImap ) {
01380       path = static_cast<KMFolderImap*>( parent )->imapPath();
01381     } else if ( parent->folderType() == KMFolderTypeCachedImap ) {
01382       path = static_cast<KMFolderCachedImap*>( parent )->imapPath();
01383     } else {
01384       // error
01385       return path;
01386     }
01387     
01388     return createImapPath( path, folderName );
01389   }
01390 
01391 
01392   bool ImapAccountBase::locallySubscribedTo( const QString& imapPath )
01393   {
01394       return mLocalSubscriptionBlackList.find( imapPath ) == mLocalSubscriptionBlackList.end();
01395   }
01396 
01397   void ImapAccountBase::changeLocalSubscription( const QString& imapPath, bool subscribe )
01398   {
01399     if ( subscribe ) {
01400       // find in blacklist and remove from it
01401       mLocalSubscriptionBlackList.erase( imapPath );
01402     } else {
01403       // blacklist
01404       mLocalSubscriptionBlackList.insert( imapPath );
01405     }
01406   }
01407 
01408 
01409   QStringList ImapAccountBase::locallyBlacklistedFolders() const
01410   {
01411       QStringList list;
01412       std::set<QString>::const_iterator it = mLocalSubscriptionBlackList.begin();
01413       std::set<QString>::const_iterator end = mLocalSubscriptionBlackList.end();
01414       for ( ; it != end ; ++it )
01415         list.append( *it );
01416       return list;
01417   }
01418 
01419   void ImapAccountBase::localBlacklistFromStringList( const QStringList &list )
01420   {
01421     for( QStringList::ConstIterator it = list.constBegin( ); it != list.constEnd( ); ++it )
01422       mLocalSubscriptionBlackList.insert( *it );
01423   }
01424 
01425 } // namespace KMail
01426 
01427 #include "imapaccountbase.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys