kmail Library API Documentation

kmfoldercachedimap.cpp

00001 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <errno.h>
00026 
00027 #include "kmkernel.h"
00028 #include "kmfoldercachedimap.h"
00029 #include "undostack.h"
00030 #include "kmfoldermgr.h"
00031 #include "kmmessage.h"
00032 #include "kmacctcachedimap.h"
00033 #include "kmacctmgr.h"
00034 #include "imapprogressdialog.h"
00035 #include "kmgroupware.h"
00036 #include "kmailicalifaceimpl.h"
00037 
00038 using KMail::CachedImapJob;
00039 using KMail::ImapAccountBase;
00040 
00041 #include <kapplication.h>
00042 #include <kmessagebox.h>
00043 #include <klocale.h>
00044 #include <kdebug.h>
00045 #include <kconfig.h>
00046 #include <kio/global.h>
00047 #include <kio/scheduler.h>
00048 #include <qbuffer.h>
00049 #include <qfile.h>
00050 #include <qlabel.h>
00051 #include <qlayout.h>
00052 #include <qvaluelist.h>
00053 
00054 #define UIDCACHE_VERSION 1
00055 
00056 
00057 DImapTroubleShootDialog::DImapTroubleShootDialog( QWidget* parent,
00058                           const char* name )
00059   : KDialogBase( Plain, i18n( "Troubleshooting the IMAP cache" ),
00060          Cancel | User1 | User2, Cancel, parent, name, true ),
00061     rc( Cancel )
00062 {
00063   QFrame* page = plainPage();
00064   QVBoxLayout *topLayout = new QVBoxLayout( page, 0 );
00065   QString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>"
00066               "<p>If you have problems with synchronizing an IMAP "
00067               "folder, you should first try rebuilding the index "
00068               "file. This will take some time to rebuild, but will "
00069               "not cause any problems.</p><p>If that is not enough, "
00070               "you can try refreshing the IMAP cache. If you do this, "
00071               "you will loose all your local changes for this folder "
00072               "and all it's subfolders.</p>" );
00073   topLayout->addWidget( new QLabel( txt, page ) );
00074   enableButtonSeparator( true );
00075 
00076   setButtonText( User1, i18n( "Refresh &Cache" ) );
00077   setButtonText( User2, i18n( "Rebuild &Index" ) );
00078 
00079   connect( this, SIGNAL( user1Clicked () ), this, SLOT( slotRebuildCache() ) );
00080   connect( this, SIGNAL( user2Clicked () ), this, SLOT( slotRebuildIndex() ) );
00081 }
00082 
00083 int DImapTroubleShootDialog::run()
00084 {
00085   DImapTroubleShootDialog d;
00086   d.exec();
00087   return d.rc;
00088 }
00089 
00090 void DImapTroubleShootDialog::slotRebuildCache()
00091 {
00092   rc = User1;
00093   done( User1 );
00094 }
00095 
00096 void DImapTroubleShootDialog::slotRebuildIndex()
00097 {
00098   rc = User2;
00099   done( User2 );
00100 }
00101 
00102 
00103 KMFolderCachedImap::KMFolderCachedImap( KMFolderDir* aParent,
00104                     const QString& aName )
00105   : KMFolderMaildir( aParent, aName ),
00106     mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ),
00107     mSubfolderState( imapNoInformation ), mIsSelected( false ),
00108     mCheckFlags( true ), mAccount( NULL ), uidMapDirty( true ),
00109     mLastUid( 0 ), uidWriteTimer( -1 ),
00110     mIsConnected( false ), mFolderRemoved( false ), mResync( false ),
00111     mSuppressDialog( false ), mHoldSyncs( false ), mRemoveRightAway( false )
00112 {
00113   KConfig* config = KMKernel::config();
00114   KConfigGroupSaver saver(config, "Folder-" + idString());
00115   if (mImapPath.isEmpty()) mImapPath = config->readEntry("ImapPath");
00116   if (aName == "INBOX" && mImapPath == "/INBOX/")
00117   {
00118     //mLabel = i18n("inbox");
00119   }
00120   mIsSystemFolder = false;
00121   mNoContent = config->readBoolEntry("NoContent", FALSE);
00122   mReadOnly = config->readBoolEntry("ReadOnly", FALSE);
00123 
00124   connect( this, SIGNAL( listMessagesComplete() ),
00125        this, SLOT( serverSyncInternal() ) );
00126 
00127   setUidValidity("");
00128   mLastUid=0;
00129   readUidCache();
00130 
00131   mProgress = 0;
00132 }
00133 
00134 KMFolderCachedImap::~KMFolderCachedImap()
00135 {
00136   if( !mFolderRemoved ) {
00137     // Only write configuration when the folder haven't been deleted
00138     KConfig* config = KMKernel::config();
00139     KConfigGroupSaver saver(config, "Folder-" + idString());
00140     config->writeEntry("ImapPath", mImapPath);
00141     config->writeEntry("NoContent", mNoContent);
00142     config->writeEntry("ReadOnly", mReadOnly);
00143 
00144     writeUidCache();
00145   }
00146 
00147   if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed(this);
00148 }
00149 
00150 int KMFolderCachedImap::remove()
00151 {
00152   mFolderRemoved = true;
00153   int rc = KMFolderMaildir::remove();
00154 
00155   if( mRemoveRightAway ) {
00156     // This is the account folder of an account that was just removed
00157     // When this happens, be sure to delete all traces of the cache
00158     QString part1 = path() + "/." + dotEscape(name());
00159     QString uidCacheFile = part1 + ".uidcache";
00160     if( QFile::exists(uidCacheFile) )
00161       unlink( QFile::encodeName( uidCacheFile ) );
00162     KIO::del( KURL( part1 + ".directory" ) );
00163   } else {
00164     // Don't remove the uidcache file here, since presence of that is how
00165     // we figure out if a directory present on the server have been deleted
00166     // from the cache or if it's new on the server. The file is removed
00167     // during the sync
00168   }
00169 
00170   return rc;
00171 }
00172 
00173 QString KMFolderCachedImap::uidCacheLocation() const
00174 {
00175   QString sLocation(path());
00176   if (!sLocation.isEmpty()) sLocation += '/';
00177   return sLocation + '.' + dotEscape(fileName()) + ".uidcache";
00178 }
00179 
00180 int KMFolderCachedImap::readUidCache()
00181 {
00182   QFile uidcache( uidCacheLocation() );
00183   if( uidcache.open( IO_ReadOnly ) ) {
00184     char buf[1024];
00185     int len = uidcache.readLine( buf, sizeof(buf) );
00186     if( len > 0 ) {
00187       int cacheVersion;
00188       sscanf( buf, "# KMail-UidCache V%d\n",  &cacheVersion );
00189       if( cacheVersion == UIDCACHE_VERSION ) {
00190     len = uidcache.readLine( buf, sizeof(buf) );
00191     if( len > 0 ) {
00192       setUidValidity( QString::fromLocal8Bit( buf).stripWhiteSpace() );
00193       len = uidcache.readLine( buf, sizeof(buf) );
00194       if( len > 0 ) {
00195         mLastUid =
00196           QString::fromLocal8Bit( buf).stripWhiteSpace().toULong();
00197         return 0;
00198       }
00199     }
00200       }
00201     }
00202   }
00203   return -1;
00204 }
00205 
00206 int KMFolderCachedImap::writeUidCache()
00207 {
00208   if( lastUid() == 0 || uidValidity().isEmpty() )
00209     // No info from the server yet
00210     return 0;
00211 
00212   QFile uidcache( uidCacheLocation() );
00213   if( uidcache.open( IO_WriteOnly ) ) {
00214     QTextStream str( &uidcache );
00215     str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl;
00216     str << uidValidity() << endl;
00217     str << lastUid() << endl;
00218     uidcache.flush();
00219     fsync( uidcache.handle() ); /* this is probably overkill */
00220     uidcache.close();
00221     return 0;
00222   } else {
00223     return errno; /* does QFile set errno? */
00224   }
00225 }
00226 
00227 void KMFolderCachedImap::reloadUidMap()
00228 {
00229   uidMap.clear();
00230   open();
00231   for( int i = 0; i < count(); ++i ) {
00232     bool unget = !isMessage(i);
00233     bool ok;
00234     KMMessage *msg = getMsg(i);
00235     if( !msg ) continue;
00236     ulong uid = msg->headerField("X-UID").toULong(&ok);
00237     if (unget) unGetMsg(i);
00238     if( ok ) {
00239       uidMap.insert( uid, i );
00240       if( uid > mLastUid ) setLastUid( uid );
00241     }
00242   }
00243   close();
00244   uidMapDirty = false;
00245 }
00246 
00247 /* Reimplemented from KMFolderMaildir */
00248 KMMessage* KMFolderCachedImap::take(int idx)
00249 {
00250   uidMapDirty = true;
00251   return KMFolderMaildir::take(idx);
00252 }
00253 
00254 // Add a message without clearing it's X-UID field.
00255 int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail,
00256                                         int* index_return )
00257 {
00258   // Possible optimization: Only dirty if not filtered below
00259   bool ok;
00260   ulong uid = msg->headerField("X-UID").toULong( &ok );
00261   if( ok ) {
00262     uidMapDirty = true;
00263     if( uid > mLastUid )
00264       setLastUid( uid );
00265   }
00266 
00267   // Add the message
00268   int rc = KMFolderMaildir::addMsg(msg, index_return);
00269 
00270   if( newMail && imapPath() == "/INBOX/" )
00271     // This is a new message. Filter it
00272     mAccount->processNewMsg( msg );
00273 
00274   return rc;
00275 }
00276 
00277 /* Reimplemented from KMFolderMaildir */
00278 int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return)
00279 {
00280   // Strip the IMAP UID
00281   msg->removeHeaderField( "X-UID" );
00282 
00283   // Add it to storage
00284   return addMsgInternal( msg, false, index_return );
00285 }
00286 
00287 
00288 /* Reimplemented from KMFolderMaildir */
00289 void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
00290 {
00291   uidMapDirty = true;
00292 
00293   // Remove it from disk
00294   KMFolderMaildir::removeMsg(idx,imapQuiet);
00295 
00296   kmkernel->dimapFolderMgr()->contentsChanged();
00297 }
00298 
00299 bool KMFolderCachedImap::canRemoveFolder() const {
00300   // If this has subfolders it can't be removed
00301   if( child() != 0 && child()->count() > 0 )
00302     return false;
00303 
00304 #if 0
00305   // No special condition here, so let base class decide
00306   return KMFolderMaildir::canRemoveFolder();
00307 #endif
00308   return true;
00309 }
00310 
00311 /* Reimplemented from KMFolderDir */
00312 int KMFolderCachedImap::rename( const QString& aName,
00313                 KMFolderDir* /*aParent*/ )
00314 {
00315   if ( aName == name() )
00316     // Stupid user trying to rename it to it's old name :)
00317     return 0;
00318 
00319   if( mSyncState != SYNC_STATE_INITIAL ) {
00320     KMessageBox::error( 0, i18n("You can't rename a folder when a sync is in progress") );
00321     return -1;
00322   }
00323 
00324   if( account() == 0 ) {
00325     QString err = i18n("You must synchronize with the server before renaming IMAP folders.");
00326     KMessageBox::error( 0, err );
00327     return -1;
00328   }
00329 
00330   CachedImapJob *job = new CachedImapJob( aName, CachedImapJob::tRenameFolder, this );
00331   job->start();
00332   return 0;
00333 }
00334 
00335 void KMFolderCachedImap::setLastUid( ulong uid )
00336 {
00337   mLastUid = uid;
00338   if( uidWriteTimer == -1 )
00339     // Write in one minute
00340     uidWriteTimer = startTimer( 60000 );
00341 }
00342 
00343 void KMFolderCachedImap::timerEvent( QTimerEvent* )
00344 {
00345   killTimer( uidWriteTimer );
00346   uidWriteTimer = -1;
00347   writeUidCache();
00348 }
00349 
00350 ulong KMFolderCachedImap::lastUid()
00351 {
00352   return mLastUid;
00353 }
00354 
00355 KMMessage* KMFolderCachedImap::findByUID( ulong uid )
00356 {
00357   bool mapReloaded = false;
00358   if( uidMapDirty ) {
00359     reloadUidMap();
00360     mapReloaded = true;
00361   }
00362 
00363   QMap<ulong,int>::Iterator it = uidMap.find( uid );
00364   if( it != uidMap.end() ) {
00365     bool unget = !isMessage(count() - 1);
00366     KMMessage* msg = getMsg( *it );
00367     if( msg && msg->headerField("X-UID").toULong() == uid )
00368       return msg;
00369     else if( unget )
00370       unGetMsg( *it );
00371   }
00372 
00373   // Not found by now
00374   if( mapReloaded )
00375     // Not here then
00376     return 0;
00377 
00378   // There could be a problem in the maps. Rebuild them and try again
00379   reloadUidMap();
00380   it = uidMap.find( uid );
00381   if( it != uidMap.end() )
00382     // Since the uid map is just rebuilt, no need for the sanity check
00383     return getMsg( *it );
00384 
00385   // Then it's not here
00386   return 0;
00387 }
00388 
00389 // This finds and sets the proper account for this folder if it has
00390 // not been done
00391 KMAcctCachedImap *KMFolderCachedImap::account()
00392 {
00393   if( (KMAcctCachedImap *)mAccount == 0 ) {
00394     // Find the account
00395     mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->find( name() ) );
00396   }
00397 
00398   return mAccount;
00399 }
00400 
00401 void KMFolderCachedImap::slotTroubleshoot()
00402 {
00403   const int rc = DImapTroubleShootDialog::run();
00404 
00405   if( rc == KDialogBase::User1 ) {
00406     // Refresh cache
00407     if( !account() ) {
00408       KMessageBox::sorry( 0, i18n("No account setup for this folder.\n"
00409                   "Please try running a sync before this.") );
00410       return;
00411     }
00412     QString str = i18n("Are you sure you want to refresh the IMAP cache of "
00413                "the folder %1 and all it's subfolders?\nThis will "
00414                "remove all changes you have done locally to your "
00415                "folders").arg( name() );
00416     QString s1 = i18n("Refresh IMAP Cache");
00417     QString s2 = i18n("&Refresh");
00418     if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) ==
00419     KMessageBox::Continue )
00420       account()->invalidateIMAPFolders( this );
00421   } else if( rc == KDialogBase::User2 ) {
00422     // Rebuild index file
00423     createIndexFromContents();
00424     KMessageBox::information( 0, i18n( "The index of this folder has been "
00425                        "recreated." ) );
00426   }
00427 }
00428 
00429 void KMFolderCachedImap::processNewMail()
00430 {
00431   if( account() )
00432     account()->processNewMail( this, true );
00433 }
00434 
00435 void KMFolderCachedImap::serverSync( bool suppressDialog )
00436 {
00437   if( mSyncState != SYNC_STATE_INITIAL ) {
00438     if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset\nit to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ) ) == KMessageBox::Yes ) {
00439       mSyncState = SYNC_STATE_INITIAL;
00440     } else return;
00441   }
00442 
00443   assert( account() );
00444 
00445   // Connect to the imap progress dialog
00446   mSuppressDialog = suppressDialog;
00447   if( mIsConnected != mAccount->isProgressDialogEnabled() &&
00448       suppressDialog )
00449   {
00450     if( !mIsConnected )
00451       connect( this, SIGNAL( newState( const QString&, int, const QString& ) ),
00452            account()->imapProgressDialog(),
00453            SLOT( syncState( const QString&, int, const QString& ) ) );
00454     else
00455       disconnect( this, SIGNAL( newState( const QString&, int, const QString& ) ),
00456            account()->imapProgressDialog(),
00457            SLOT( syncState( const QString&, int, const QString& ) ) );
00458     mIsConnected = mAccount->isProgressDialogEnabled();
00459   }
00460 
00461   if( mHoldSyncs ) {
00462     // All done for this folder.
00463     mProgress = 100; // all done
00464     emit newState( name(), mProgress, i18n("Synchronization skipped"));
00465     mAccount->displayProgress();
00466     mSyncState = SYNC_STATE_INITIAL;
00467     emit statusMsg( i18n("%1: Synchronization done").arg(name()) );
00468     emit folderComplete( this, true );
00469     return;
00470   }
00471 
00472   mResync = false;
00473   serverSyncInternal();
00474 }
00475 
00476 QString KMFolderCachedImap::state2String( int state ) const
00477 {
00478   switch( state ) {
00479   case SYNC_STATE_INITIAL:           return "SYNC_STATE_INITIAL";
00480   case SYNC_STATE_PUT_MESSAGES:      return "SYNC_STATE_PUT_MESSAGES";
00481   case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
00482   case SYNC_STATE_LIST_SUBFOLDERS:   return "SYNC_STATE_LIST_SUBFOLDERS";
00483   case SYNC_STATE_LIST_SUBFOLDERS2:  return "SYNC_STATE_LIST_SUBFOLDERS2";
00484   case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
00485   case SYNC_STATE_LIST_MESSAGES:     return "SYNC_STATE_LIST_MESSAGES";
00486   case SYNC_STATE_DELETE_MESSAGES:   return "SYNC_STATE_DELETE_MESSAGES";
00487   case SYNC_STATE_GET_MESSAGES:      return "SYNC_STATE_GET_MESSAGES";
00488   case SYNC_STATE_FIND_SUBFOLDERS:   return "SYNC_STATE_FIND_SUBFOLDERS";
00489   case SYNC_STATE_SYNC_SUBFOLDERS:   return "SYNC_STATE_SYNC_SUBFOLDERS";
00490   case SYNC_STATE_EXPUNGE_MESSAGES:  return "SYNC_STATE_EXPUNGE_MESSAGES";
00491   case SYNC_STATE_HANDLE_INBOX:      return "SYNC_STATE_HANDLE_INBOX";
00492   case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
00493   default:                           return "Unknown state";
00494   }
00495 }
00496 
00497 
00498 // While the server synchronization is running, mSyncState will hold
00499 // the state that should be executed next
00500 void KMFolderCachedImap::serverSyncInternal()
00501 {
00502   switch( mSyncState ) {
00503   case SYNC_STATE_INITIAL:
00504   {
00505     mProgress = 0;
00506     emit statusMsg( i18n("%1: Synchronizing").arg(name()) );
00507     emit newState( name(), mProgress, i18n("Synchronizing"));
00508 
00509     open();
00510 
00511     kdDebug(5006) << k_funcinfo << " making connection" << endl;
00512     // Connect to the server (i.e. prepare the slave)
00513     ImapAccountBase::ConnectionState cs = mAccount->makeConnection();
00514     if ( cs == ImapAccountBase::Error ) {
00515       // Cancelled by user, or slave can't start
00516       kdDebug(5006) << "makeConnection said Error, aborting." << endl;
00517       // We stop here. We're already in SYNC_STATE_INITIAL for the next time.
00518       emit folderComplete(this, FALSE);
00519       break;
00520     } else if ( cs == ImapAccountBase::Connecting )
00521     {
00522       kdDebug(5006) << "makeConnection said Connecting, waiting for signal."
00523             << endl;
00524       // We'll wait for the connectionResult signal from the account.
00525       connect( mAccount, SIGNAL( connectionResult(int) ),
00526            this, SLOT( slotConnectionResult(int) ) );
00527       break;
00528     } else // Connected
00529     {
00530         kdDebug(5006) << "makeConnection said Connected, proceeding." << endl;
00531         mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
00532         // Fall through to next state
00533     }
00534   }
00535   case SYNC_STATE_CHECK_UIDVALIDITY:
00536     emit syncRunning( this, true );
00537     mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
00538     if( !noContent() ) {
00539       // TODO (Bo): How can we obtain the UID validity on a noContent folder?
00540       // TODO (Bo): Is it perhaps not necessary to do so?
00541       checkUidValidity();
00542       break;
00543     }
00544     // Else carry on
00545 
00546   case SYNC_STATE_CREATE_SUBFOLDERS:
00547     mSyncState = SYNC_STATE_PUT_MESSAGES;
00548     createNewFolders();
00549     break;
00550 
00551   case SYNC_STATE_PUT_MESSAGES:
00552     mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
00553     if( !noContent() ) {
00554       uploadNewMessages();
00555       break;
00556     }
00557     // Else carry on
00558 
00559   case SYNC_STATE_LIST_SUBFOLDERS:
00560     mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
00561     mProgress += 10;
00562     emit statusMsg( i18n("%1: Retrieving folderlist").arg(name()) );
00563     emit newState( name(), mProgress, i18n("Retrieving folderlist"));
00564     if( !listDirectory() ) {
00565       mSyncState = SYNC_STATE_INITIAL;
00566       KMessageBox::error(0, i18n("Error during listDirectory()"));
00567     }
00568     break;
00569 
00570   case SYNC_STATE_LIST_SUBFOLDERS2:
00571     mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
00572     mProgress += 10;
00573     emit newState( name(), mProgress, i18n("Retrieving subfolders"));
00574     listDirectory2();
00575     break;
00576 
00577   case SYNC_STATE_DELETE_SUBFOLDERS:
00578     mSyncState = SYNC_STATE_LIST_MESSAGES;
00579     emit syncState( SYNC_STATE_DELETE_SUBFOLDERS, foldersForDeletionOnServer.count() );
00580     if( !foldersForDeletionOnServer.isEmpty() ) {
00581       emit statusMsg( i18n("%1: Deleting folders %2 from server").arg(name())
00582               .arg( foldersForDeletionOnServer.join(", ") ) );
00583       emit newState( name(), mProgress, i18n("Deleting folders from server"));
00584       CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
00585                           CachedImapJob::tDeleteFolders, this );
00586       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00587       job->start();
00588       break;
00589     } else
00590       emit newState( name(), mProgress, i18n("No folders to delete from server"));
00591       // Carry on
00592 
00593   case SYNC_STATE_LIST_MESSAGES:
00594     mSyncState = SYNC_STATE_DELETE_MESSAGES;
00595     mProgress += 10;
00596     if( !noContent() ) {
00597       emit statusMsg( i18n("%1: Retrieving messagelist").arg(name()) );
00598       emit newState( name(), mProgress, i18n("Retrieving messagelist"));
00599       // emit syncState( SYNC_STATE_LIST_MESSAGES, foldersForDeletionOnServer.count() );
00600       listMessages();
00601       break;
00602     }
00603     // Else carry on
00604 
00605   case SYNC_STATE_DELETE_MESSAGES:
00606     mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
00607     if( !noContent() ) {
00608       if( deleteMessages() ) {
00609     // Fine, we will continue with the next state
00610       } else {
00611     // No messages to delete, skip to GET_MESSAGES
00612     emit newState( name(), mProgress, i18n("No messages to delete..."));
00613     mSyncState = SYNC_STATE_GET_MESSAGES;
00614     serverSyncInternal();
00615       }
00616       break;
00617     }
00618     // Else carry on
00619 
00620   case SYNC_STATE_EXPUNGE_MESSAGES:
00621     mSyncState = SYNC_STATE_GET_MESSAGES;
00622     if( !noContent() ) {
00623       mProgress += 10;
00624       emit statusMsg( i18n("%1: Expunging deleted messages").arg(name()) );
00625       emit newState( name(), mProgress, i18n("Expunging deleted messages"));
00626       CachedImapJob *job = new CachedImapJob( QString::null,
00627                           CachedImapJob::tExpungeFolder, this );
00628       connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00629       job->start();
00630       break;
00631     }
00632     // Else carry on
00633 
00634   case SYNC_STATE_GET_MESSAGES:
00635     mSyncState = SYNC_STATE_HANDLE_INBOX;
00636     if( !noContent() ) {
00637       //emit syncState( SYNC_STATE_GET_MESSAGES, mMsgsForDownload.count() );
00638       if( !mMsgsForDownload.isEmpty() ) {
00639     emit statusMsg( i18n("%1: Retrieving new messages").arg(name()) );
00640     emit newState( name(), mProgress, i18n("Retrieving new messages"));
00641     CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
00642                                                 CachedImapJob::tGetMessage,
00643                                                 this );
00644     connect( job, SIGNAL( progress(unsigned long, unsigned long) ),
00645                  this, SLOT( slotProgress(unsigned long, unsigned long) ) );
00646     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00647         job->start();
00648     mMsgsForDownload.clear();
00649     break;
00650       } else {
00651     emit newState( name(), mProgress, i18n("No new messages from server"));
00652       }
00653     }
00654     // Else carry on
00655 
00656   case SYNC_STATE_HANDLE_INBOX:
00657     // Wrap up the 'download emails' stage (which has a 20% span)
00658     mProgress += 20;
00659     //kdDebug() << name() << ": +20 -> " << mProgress << "%" << endl;
00660 
00661     if( mResync ) {
00662       // Some conflict have been resolved, so restart the sync
00663       mResync = false;
00664       mSyncState = SYNC_STATE_INITIAL;
00665       serverSyncInternal();
00666       break;
00667     } else
00668       // Continue with the subfolders
00669       mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
00670 
00671   case SYNC_STATE_FIND_SUBFOLDERS:
00672     {
00673       emit newState( name(), mProgress, i18n("Updating cache file"));
00674 
00675       mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
00676       mSubfoldersForSync.clear();
00677       mCurrentSubfolder = 0;
00678       if( child() ) {
00679     KMFolderNode *node = child()->first();
00680     while( node ) {
00681       if( !node->isDir() ) {
00682             if ( !static_cast<KMFolderCachedImap*>(node)->imapPath().isEmpty() )
00683           // Only sync folders that have been accepted by the server
00684           mSubfoldersForSync << static_cast<KMFolderCachedImap*>(node);
00685       }
00686       node = child()->next();
00687     }
00688       }
00689     }
00690 
00691     // All done for this folder.
00692     mProgress = 100; // all done
00693     emit newState( name(), mProgress, i18n("Synchronization done"));
00694     emit syncRunning( this, false );
00695     mAccount->displayProgress();
00696     // Carry on
00697 
00698   case SYNC_STATE_SYNC_SUBFOLDERS:
00699     {
00700       if( mCurrentSubfolder ) {
00701     disconnect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
00702             this, SLOT( serverSyncInternal() ) );
00703     mCurrentSubfolder = 0;
00704       }
00705 
00706       if( mSubfoldersForSync.isEmpty() ) {
00707     mSyncState = SYNC_STATE_INITIAL;
00708     emit statusMsg( i18n("%1: Synchronization done").arg(name()) );
00709     emit folderComplete( this, TRUE );
00710     close();
00711       } else {
00712     mCurrentSubfolder = mSubfoldersForSync.front();
00713     mSubfoldersForSync.pop_front();
00714     connect( mCurrentSubfolder, SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
00715          this, SLOT( serverSyncInternal() ) );
00716 
00717     // kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
00718     assert( !mCurrentSubfolder->imapPath().isEmpty() );
00719     mCurrentSubfolder->setAccount( account() );
00720     mCurrentSubfolder->serverSync( mSuppressDialog );
00721       }
00722     }
00723     break;
00724 
00725   default:
00726     kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
00727           << mSyncState << endl;
00728   }
00729 }
00730 
00731 /* Connected to the imap account's connectionResult signal.
00732    Emitted when the slave connected or failed to connect.
00733 */
00734 void KMFolderCachedImap::slotConnectionResult( int errorCode )
00735 {
00736   kdDebug(5006) << k_funcinfo << errorCode << endl;
00737   disconnect( mAccount, SIGNAL( connectionResult(int) ),
00738           this, SLOT( slotConnectionResult(int) ) );
00739   if ( !errorCode ) {
00740     // Success
00741     mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
00742     serverSyncInternal();
00743   } else {
00744     // Error (error message already shown by the account)
00745     emit folderComplete(this, FALSE);
00746   }
00747 }
00748 
00749 /* find new messages (messages without a UID) */
00750 QValueList<unsigned long> KMFolderCachedImap::findNewMessages()
00751 {
00752   QValueList<unsigned long> result;
00753   for( int i = 0; i < count(); ++i ) {
00754     bool unget = !isMessage(i);
00755     KMMessage *msg = getMsg(i);
00756     if( !msg ) continue; /* what goes on if getMsg() returns 0? */
00757     if( msg->headerField("X-UID").isEmpty() ) {
00758       result.append( msg->getMsgSerNum() );
00759     } else {
00760       if (unget) unGetMsg(i);
00761     }
00762   }
00763   return result;
00764 }
00765 
00766 /* Upload new messages to server */
00767 void KMFolderCachedImap::uploadNewMessages()
00768 {
00769   QValueList<unsigned long> newMsgs = findNewMessages();
00770   emit syncState( SYNC_STATE_PUT_MESSAGES, newMsgs.count() );
00771   mProgress += 10;
00772   //kdDebug() << name() << ": +10 (uploadNewMessages) -> " << mProgress << "%" << endl;
00773   if( !newMsgs.isEmpty() ) {
00774     emit statusMsg( i18n("%1: Uploading messages to server").arg(name()) );
00775 
00776     emit newState( name(), mProgress, i18n("Uploading messages to server"));
00777     CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
00778     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00779     job->start();
00780   } else {
00781     emit newState( name(), mProgress, i18n("No messages to upload to server"));
00782 
00783     serverSyncInternal();
00784   }
00785 }
00786 
00787 /* Upload new folders to server */
00788 void KMFolderCachedImap::createNewFolders()
00789 {
00790   QValueList<KMFolderCachedImap*> newFolders = findNewFolders();
00791   //emit syncState( SYNC_STATE_CREATE_SUBFOLDERS, newFolders.count() );
00792   mProgress += 10;
00793   //kdDebug() << name() << ": +10 (createNewFolders) -> " << mProgress << "%" << endl;
00794   if( !newFolders.isEmpty() ) {
00795     emit statusMsg( i18n("%1: Creating subfolders on server").arg(name()) );
00796     emit newState( name(), mProgress, i18n("Creating subfolders on server"));
00797     CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
00798     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00799     job->start();
00800   } else {
00801     serverSyncInternal();
00802   }
00803 }
00804 
00805 QValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
00806 {
00807   QValueList<KMFolderCachedImap*> newFolders;
00808   if( child() ) {
00809     KMFolderNode *node = child()->first();
00810     while( node ) {
00811       if( !node->isDir() ) {
00812     if( !node->isA("KMFolderCachedImap") ) {
00813       kdDebug(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
00814             << node->name() << " is not an IMAP folder. It is a "
00815             << node->className() << endl;
00816       node = child()->next();
00817       assert(0);
00818     }
00819     KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(node);
00820     if( folder->imapPath().isEmpty() ) newFolders << folder;
00821       }
00822       node = child()->next();
00823     }
00824   }
00825   return newFolders;
00826 }
00827 
00828 bool KMFolderCachedImap::deleteMessages()
00829 {
00830   /* Delete messages from cache that are gone from the server */
00831   QPtrList<KMMessage> msgsForDeletion;
00832 
00833   // It is not possible to just go over all indices and remove
00834   // them one by one because the index list can get resized under
00835   // us. So use msg pointers instead
00836   for( int i = 0; i < count(); ++i ) {
00837     bool unget = !isMessage(i);
00838     KMMessage *msg = getMsg(i);
00839     if( !msg ) continue;
00840     bool ok;
00841     ulong uid = msg->headerField( "X-UID" ).toULong( &ok );
00842     if( ok && !uidsOnServer.contains( uid ) )
00843       msgsForDeletion.append( msg );
00844     else
00845       if (unget) unGetMsg(i);
00846   }
00847 
00848   if( !msgsForDeletion.isEmpty() ) {
00849     emit statusMsg( i18n("%1: Deleting removed messages from cache").arg(name()) );
00850     removeMsg( msgsForDeletion );
00851   }
00852 
00853   mProgress += 10;
00854   //kdDebug() << name() << ": +10 (deleteMessages) -> " << mProgress << "%" << endl;
00855   emit newState( name(), mProgress, i18n("Deleting removed messages from server"));
00856 
00857   /* Delete messages from the server that we dont have anymore */
00858   if( !uidsForDeletionOnServer.isEmpty() ) {
00859     emit statusMsg( i18n("%1: Deleting removed messages from server").arg(name()) );
00860     QStringList sets = makeSets( uidsForDeletionOnServer, true );
00861     uidsForDeletionOnServer.clear();
00862     if( sets.count() > 1 ) {
00863       // Rerun the sync until the messages are all deleted
00864       mResync = true;
00865     }
00866     //kdDebug(5006) << "Deleting " << sets.front() << " from sever folder " << imapPath() << endl;
00867     CachedImapJob *job = new CachedImapJob( sets.front(), CachedImapJob::tDeleteMessage,
00868                         this );
00869     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00870     job->start();
00871     return true;
00872   } else {
00873     return false;
00874   }
00875 }
00876 
00877 void KMFolderCachedImap::checkUidValidity() {
00878   // IMAP root folders don't seem to have a UID validity setting.
00879   // Also, don't try the uid validity on new folders
00880   if( imapPath().isEmpty() || imapPath() == "/" )
00881     // Just proceed
00882     serverSyncInternal();
00883   else {
00884     mProgress += 10;
00885     //kdDebug() << name() << ": +10 (checkUidValidity) -> " << mProgress << "%" << endl;
00886     emit newState( name(), mProgress, i18n("Checking folder validity"));
00887     emit statusMsg( i18n("%1: Checking folder validity").arg(name()) );
00888     CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
00889     connect( job, SIGNAL( finished() ), this, SLOT( serverSyncInternal() ) );
00890     job->start();
00891   }
00892 }
00893 
00894 /* This will only list the messages in a folder.
00895    No directory listing done*/
00896 void KMFolderCachedImap::listMessages() {
00897   if( imapPath() == "/" ) {
00898     // Don't list messages on the root folder
00899     serverSyncInternal();
00900     return;
00901   }
00902 
00903   if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
00904     emit listMessagesComplete();
00905     emit folderComplete( this, false );
00906     return;
00907   }
00908   uidsOnServer.clear();
00909   uidsForDeletionOnServer.clear();
00910   mMsgsForDownload.clear();
00911   mUidsForDownload.clear();
00912   KURL url = mAccount->getUrl();
00913   url.setPath(imapPath() + ";UID=1:*;SECTION=ENVELOPE");
00914   KMAcctCachedImap::jobData jd( url.url(), this );
00915 
00916   KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
00917   KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
00918   mAccount->insertJob(newJob, jd);
00919 
00920   connect( newJob, SIGNAL( result( KIO::Job* ) ),
00921        this, SLOT( slotGetLastMessagesResult( KIO::Job* ) ) );
00922   connect( newJob, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
00923        this, SLOT( slotGetMessagesData( KIO::Job* , const QByteArray& ) ) );
00924 }
00925 
00926 void KMFolderCachedImap::slotGetLastMessagesResult(KIO::Job * job)
00927 {
00928   getMessagesResult(job, true);
00929 }
00930 
00931 
00932 //-----------------------------------------------------------------------------
00933 void KMFolderCachedImap::slotGetMessagesResult(KIO::Job * job)
00934 {
00935   getMessagesResult(job, false);
00936 }
00937 
00938 // All this should be moved to CachedImapJob obviously...
00939 void KMFolderCachedImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
00940 {
00941   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00942   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
00943     kdDebug(5006) << "could not find job!?!?!" << endl;
00944     serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
00945     return;
00946   }
00947   (*it).cdata += QCString(data, data.size() + 1);
00948   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
00949   if (pos > 0) {
00950     int p = (*it).cdata.find("\r\nX-uidValidity:");
00951     if (p != -1)
00952       setUidValidity((*it).cdata.mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
00953     (*it).cdata.remove(0, pos);
00954   }
00955   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
00956 
00957   int flags;
00958   while (pos >= 0) {
00959     KMMessage *msg = new KMMessage;
00960     msg->fromString((*it).cdata.mid(16, pos - 16));
00961     flags = msg->headerField("X-Flags").toInt();
00962     bool ok;
00963     ulong uid = msg->headerField("X-UID").toULong(&ok);
00964     if( ok ) uidsOnServer.append( uid );
00965     if ( /*flags & 8 ||*/ uid <= lastUid()) {
00966       // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<name()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
00967       /* If this message UID is not present locally, then it must
00968           have been deleted by the user, so we delete it on the
00969           server also.
00970       */
00971       KMMsgBase *existingMessage = findByUID(uid);
00972       if( !existingMessage ) {
00973          // kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
00974          uidsForDeletionOnServer << uid;
00975       } else {
00976          /* The message is OK, update flags */
00977          flagsToStatus( existingMessage, flags );
00978       }
00979       delete msg;
00980     } else {
00981       ulong size = msg->headerField("X-Length").toULong();
00982       mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
00983       if( imapPath() == "/INBOX/" )
00984          mUidsForDownload << uid;
00985 
00986       delete msg;
00987     }
00988     (*it).cdata.remove(0, pos);
00989     (*it).done++;
00990     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
00991     mAccount->displayProgress();
00992   }
00993 }
00994 
00995 void KMFolderCachedImap::getMessagesResult( KIO::Job * job, bool lastSet )
00996 {
00997 
00998   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
00999   if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
01000     kdDebug(5006) << "could not find job!?!?!" << endl;
01001     serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
01002     return;
01003   }
01004 
01005   if( job->error() ) {
01006     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01007                   job->errorText() );
01008     mContentState = imapNoInformation;
01009     emit folderComplete(this, FALSE);
01010   } else if (lastSet) mContentState = imapFinished;
01011   mAccount->removeJob(it);
01012   if( lastSet )
01013     emit listMessagesComplete();
01014 }
01015 
01016 void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
01017 {
01018   //kdDebug() << "KMFolderCachedImap::slotProgress done=" << done << " total=" << total << "=> progress=" << mProgress + ( 20 * done ) / total << endl;
01019   // Progress info while retrieving new emails
01020   // (going from mProgress to mProgress+20)
01021   emit newState( name(), mProgress + (20 * done) / total, QString::null);
01022 }
01023 
01024 //-----------------------------------------------------------------------------
01025 void KMFolderCachedImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg)
01026 {
01027   if (flags & 4) 
01028     msg->setStatus( KMMsgStatusFlag );
01029   if (flags & 2)
01030     msg->setStatus( KMMsgStatusReplied );
01031   if (flags & 1)
01032     msg->setStatus( KMMsgStatusOld );
01033 
01034   if (msg->isOfUnknownStatus()) {
01035     if (newMsg)
01036       msg->setStatus( KMMsgStatusNew );
01037     else
01038       msg->setStatus( KMMsgStatusUnread );
01039   }
01040 }
01041 
01042 
01043 void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
01044 {
01045   assert( aAccount->isA("KMAcctCachedImap") );
01046   mAccount = aAccount;
01047   if( imapPath()=="/" ) aAccount->setFolder(this);
01048 
01049   if( !mChild || mChild->count() == 0) return;
01050   for( KMFolderNode* node = mChild->first(); node; node = mChild->next() )
01051     if (!node->isDir())
01052       static_cast<KMFolderCachedImap*>(node)->setAccount(aAccount);
01053 }
01054 
01055 
01056 // This synchronizes the subfolders with the server
01057 bool KMFolderCachedImap::listDirectory()
01058 {
01059   mSubfolderState = imapInProgress;
01060   KURL url = mAccount->getUrl();
01061   url.setPath(imapPath() + ";TYPE="
01062           + (mAccount->onlySubscribedFolders() ? "LSUB" : "LIST"));
01063   KMAcctCachedImap::jobData jd( url.url(), this );
01064   mSubfolderNames.clear();
01065   mSubfolderPaths.clear();
01066   mSubfolderMimeTypes.clear();
01067 
01068   if( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01069     emit folderComplete( this, false );
01070     return false;
01071   }
01072 
01073   KIO::SimpleJob *job = KIO::listDir(url, FALSE);
01074   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01075   mAccount->insertJob(job, jd);
01076   connect(job, SIGNAL(result(KIO::Job *)),
01077           this, SLOT(slotListResult(KIO::Job *)));
01078   connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
01079           this, SLOT(slotListEntries(KIO::Job *, const KIO::UDSEntryList &)));
01080 
01081   return TRUE;
01082 }
01083 
01084 void KMFolderCachedImap::slotListResult(KIO::Job * job)
01085 {
01086   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01087   if( it == mAccount->jobsEnd() ) { // Shouldn't happen
01088     kdDebug(5006) << "could not find job!?!?!" << endl;
01089     serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
01090     return;
01091   }
01092 
01093   if( job->error() ) {
01094     kdDebug(5006) << "listDirectory() - slotListResult: Job error\n";
01095     mAccount->slotSlaveError( mAccount->slave(), job->error(), job->errorText() );
01096   }
01097 
01098   mSubfolderState = imapFinished;
01099   mAccount->removeJob(it);
01100 
01101   if (!job->error()) {
01102     kmkernel->dimapFolderMgr()->quiet(TRUE);
01103     createChildFolder();
01104 
01105     // Find all subfolders present on disk but not on the server
01106     KMFolderCachedImap *folder;
01107     KMFolderNode *node = mChild->first();
01108     QPtrList<KMFolder> toRemove;
01109     while (node) {
01110       if (!node->isDir() ) {
01111     if( mSubfolderNames.findIndex(node->name()) == -1) {
01112       // This subfolder isn't present on the server
01113       kdDebug(5006) << node->name() << " isn't on the server." << endl;
01114       folder = static_cast<KMFolderCachedImap*>(node);
01115       if( !folder->uidValidity().isEmpty() ) {
01116         // The folder have a uidValidity setting, so it has been on the
01117         // server before. Delete it locally.
01118         toRemove.append( folder );
01119       }
01120     }
01121       }
01122       node = mChild->next();
01123     }
01124     // Remove all folders
01125     for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() )
01126       kmkernel->dimapFolderMgr()->remove( doomed );
01127 
01128     mAccount->displayProgress();
01129     serverSyncInternal();
01130   }
01131 }
01132 
01133 
01134 void KMFolderCachedImap::listDirectory2() {
01135   foldersForDeletionOnServer.clear();
01136 
01137   // Find all subfolders present on server but not on disk
01138   for (uint i = 0; i < mSubfolderNames.count(); i++) {
01139     KMFolderCachedImap *folder = 0;
01140 
01141     // Find the subdir, if already present
01142     KMFolderNode *node;
01143     for (node = mChild->first(); node; node = mChild->next())
01144       if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
01145 
01146     if (!node) {
01147       // This folder is not present here
01148       QString part1 = path() + "/." + dotEscape(name()) + ".directory/."
01149     + dotEscape(mSubfolderNames[i]);
01150       QString uidCacheFile = part1 + ".uidcache";
01151       if( QFile::exists(uidCacheFile) ) {
01152     // This is an old folder that is deleted locally - delete it on the server
01153     unlink( QFile::encodeName( uidCacheFile ) );
01154     foldersForDeletionOnServer << mSubfolderPaths[i];
01155     // Make sure all trace of the dir is gone
01156     KIO::del( KURL( part1 + ".directory" ) );
01157       } else {
01158     // This is a new folder, create the local cache
01159     folder = static_cast<KMFolderCachedImap*>
01160       (mChild->createFolder(mSubfolderNames[i], false, KMFolderTypeCachedImap));
01161     if (folder) {
01162       folder->close();
01163       folder->setAccount(mAccount);
01164       kmkernel->dimapFolderMgr()->contentsChanged();
01165     } else {
01166       kdDebug(5006) << "can't create folder " << mSubfolderNames[i] <<endl;
01167     }
01168       }
01169     } else {
01170       // kdDebug(5006) << "node " << node->name() << " is a " << node->className() << endl;
01171       if( node->isA("KMFolderCachedImap") )
01172     folder = static_cast<KMFolderCachedImap*>(node);
01173     }
01174 
01175     if( folder && folder->imapPath().isEmpty() ) {
01176       // kdDebug(5006) << "folder("<<folder->name()<<")->imapPath()=" << folder->imapPath()
01177       // << "\nAssigning new imapPath " << mSubfolderPaths[i] << endl;
01178       // Write folder settings
01179       folder->setAccount(mAccount);
01180       folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
01181       folder->setImapPath(mSubfolderPaths[i]);
01182     }
01183   }
01184 
01185   kmkernel->dimapFolderMgr()->quiet(FALSE);
01186   emit listComplete(this);
01187   serverSyncInternal();
01188 }
01189 
01190 
01191 //-----------------------------------------------------------------------------
01192 void KMFolderCachedImap::slotListEntries(KIO::Job * job, const KIO::UDSEntryList & uds)
01193 {
01194   // kdDebug(5006) << "KMFolderCachedImap::slotListEntries("<<name()<<")" << endl;
01195   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01196   if (it == mAccount->jobsEnd()) return;
01197 
01198   QString name;
01199   KURL url;
01200   QString mimeType;
01201   for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
01202     udsIt != uds.end(); udsIt++)
01203   {
01204     // kdDebug(5006) << "slotListEntries start" << endl;
01205     mimeType = QString::null;
01206 
01207     // Find the info on this subfolder
01208     for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
01209       eIt != (*udsIt).end(); eIt++)
01210     {
01211       //kdDebug(5006) << "slotListEntries got type " << (*eIt).m_uds << " str " << (*eIt).m_str << endl;
01212       if ((*eIt).m_uds == KIO::UDS_NAME)
01213         name = (*eIt).m_str;
01214       else if ((*eIt).m_uds == KIO::UDS_URL)
01215         url = KURL((*eIt).m_str, 106); // utf-8
01216       else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
01217         mimeType = (*eIt).m_str;
01218     }
01219 
01220     // kdDebug(5006) << "slotListEntries end. mimetype = " << mimeType
01221     //      << ", name = " << name << ", path = " << url.path() << endl;
01222 
01223     // If this was a subfolder, add it to the list
01224     if ((mimeType == "inode/directory" || mimeType == "message/digest"
01225         || mimeType == "message/directory")
01226         && name != ".." && (mAccount->hiddenFolders() || name.at(0) != '.'))
01227     {
01228       // Some servers send _lots_ of duplicates
01229       if (mSubfolderNames.findIndex(name) == -1) {
01230     mSubfolderNames.append(name);
01231     mSubfolderPaths.append(url.path());
01232     mSubfolderMimeTypes.append(mimeType);
01233       }
01234     }
01235   }
01236 }
01237 
01238 void KMFolderCachedImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01239 {
01240   KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
01241   if (it == mAccount->jobsEnd()) return;
01242   QBuffer buff((*it).data);
01243   buff.open(IO_WriteOnly | IO_Append);
01244   buff.writeBlock(data.data(), data.size());
01245   buff.close();
01246 }
01247 
01248 
01249 QStringList KMFolderCachedImap::makeSets(QStringList& uids, bool sort)
01250 {
01251   QValueList<ulong> tmp;
01252   for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it )
01253     tmp.append( (*it).toInt() );
01254   return makeSets(tmp, sort);
01255 }
01256 
01257 QStringList KMFolderCachedImap::makeSets(QValueList<ulong>& uids, bool sort)
01258 {
01259   QStringList sets;
01260   QString set;
01261 
01262   if (uids.size() == 1)
01263   {
01264     sets.append(QString::number(uids.first()));
01265     return sets;
01266   }
01267 
01268   if (sort) qHeapSort(uids);
01269 
01270   ulong last = 0;
01271   // needed to make a uid like 124 instead of 124:124
01272   bool inserted = false;
01273   /* iterate over uids and build sets like 120:122,124,126:150 */
01274   for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
01275   {
01276     if (it == uids.begin() || set.isEmpty()) {
01277       set = QString::number(*it);
01278       inserted = true;
01279     } else
01280     {
01281       if (last+1 != *it)
01282       {
01283         // end this range
01284         if (inserted)
01285           set += ',' + QString::number(*it);
01286         else
01287           set += ':' + QString::number(last) + ',' + QString::number(*it);
01288         inserted = true;
01289         if (set.length() > 100)
01290         {
01291           // just in case the server has a problem with longer lines..
01292           sets.append(set);
01293           set = "";
01294         }
01295       } else {
01296         inserted = false;
01297       }
01298     }
01299     last = *it;
01300   }
01301   // last element
01302   if (!inserted)
01303     set += ':' + QString::number(uids.last());
01304 
01305   if (!set.isEmpty()) sets.append(set);
01306 
01307   return sets;
01308 }
01309 
01310 FolderJob*
01311 KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
01312                                  QString, const AttachmentStrategy* ) const
01313 {
01314   QPtrList<KMMessage> msgList;
01315   msgList.append( msg );
01316   CachedImapJob *job = new CachedImapJob( msgList, jt, static_cast<KMFolderCachedImap*>( folder ) );
01317   return job;
01318 }
01319 
01320 FolderJob*
01321 KMFolderCachedImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01322                                  FolderJob::JobType jt, KMFolder *folder ) const
01323 {
01324   //FIXME: how to handle sets here?
01325   Q_UNUSED( sets );
01326   CachedImapJob *job = new CachedImapJob( msgList, jt, static_cast<KMFolderCachedImap*>( folder ) );
01327   return job;
01328 }
01329 
01330 #include "kmfoldercachedimap.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:26 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003