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