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