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