kmail

kmacctcachedimap.cpp
1 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include "kmacctcachedimap.h"
37 using KMail::SieveConfig;
38 
39 #include "kmfoldertree.h"
40 #include "kmfoldermgr.h"
41 #include "kmfiltermgr.h"
42 #include "kmfoldercachedimap.h"
43 #include "kmmainwin.h"
44 #include "accountmanager.h"
46 #include "progressmanager.h"
47 
48 #include <tdeio/passdlg.h>
49 #include <tdeio/scheduler.h>
50 #include <tdeio/slave.h>
51 #include <kdebug.h>
52 #include <kstandarddirs.h>
53 #include <tdeapplication.h>
54 #include <tdeconfig.h>
55 
56 #include <tqstylesheet.h>
57 
58 KMAcctCachedImap::KMAcctCachedImap( AccountManager* aOwner,
59  const TQString& aAccountName, uint id )
60  : KMail::ImapAccountBase( aOwner, aAccountName, id ), mFolder( 0 ),
61  mAnnotationCheckPassed(false),
62  mGroupwareType( GroupwareKolab ),
63  mSentCustomLoginCommand(false)
64 {
65  // Never EVER set this for the cached IMAP account
66  mAutoExpunge = false;
67 }
68 
69 
70 //-----------------------------------------------------------------------------
71 KMAcctCachedImap::~KMAcctCachedImap()
72 {
73  killAllJobsInternal( true );
74 }
75 
76 
77 //-----------------------------------------------------------------------------
78 TQString KMAcctCachedImap::type() const
79 {
80  return "cachedimap";
81 }
82 
83 void KMAcctCachedImap::init() {
84  ImapAccountBase::init();
85 }
86 
87 //-----------------------------------------------------------------------------
88 void KMAcctCachedImap::pseudoAssign( const KMAccount * a ) {
89  killAllJobs( true );
90  if (mFolder)
91  {
92  mFolder->setContentState(KMFolderCachedImap::imapNoInformation);
93  mFolder->setSubfolderState(KMFolderCachedImap::imapNoInformation);
94  }
95  ImapAccountBase::pseudoAssign( a );
96 }
97 
98 //-----------------------------------------------------------------------------
99 void KMAcctCachedImap::setImapFolder(KMFolderCachedImap *aFolder)
100 {
101  mFolder = aFolder;
102  mFolder->setImapPath( "/" );
103  mFolder->setAccount( this );
104 }
105 
106 
107 //-----------------------------------------------------------------------------
108 void KMAcctCachedImap::setAutoExpunge( bool /*aAutoExpunge*/ )
109 {
110  // Never EVER set this for the cached IMAP account
111  mAutoExpunge = false;
112 }
113 
114 //-----------------------------------------------------------------------------
115 void KMAcctCachedImap::killAllJobs( bool disconnectSlave )
116 {
117  //kdDebug(5006) << "killAllJobs: disconnectSlave=" << disconnectSlave << " " << mapJobData.count() << " jobs in map." << endl;
118  TQValueList<KMFolderCachedImap*> folderList = killAllJobsInternal( disconnectSlave );
119  for( TQValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
120  KMFolderCachedImap *fld = *it;
121  fld->resetSyncState();
122  fld->setContentState(KMFolderCachedImap::imapNoInformation);
123  fld->setSubfolderState(KMFolderCachedImap::imapNoInformation);
124  fld->sendFolderComplete(false);
125  }
126 }
127 
128 //-----------------------------------------------------------------------------
129 // Common between killAllJobs and the destructor - which shouldn't call sendFolderComplete
130 TQValueList<KMFolderCachedImap*> KMAcctCachedImap::killAllJobsInternal( bool disconnectSlave )
131 {
132  // Make list of folders to reset. This must be done last, since folderComplete
133  // can trigger the next queued mail check already.
134  TQValueList<KMFolderCachedImap*> folderList;
135  TQMap<TDEIO::Job*, jobData>::Iterator it = mapJobData.begin();
136  for (; it != mapJobData.end(); ++it) {
137  if ((*it).parent)
138  folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage());
139  // Kill the job - except if it's the one that already died and is calling us
140  if ( !it.key()->error() && mSlave ) {
141  it.key()->kill();
142  mSlave = 0; // killing a job, kills the slave
143  }
144  }
145  mapJobData.clear();
146 
147  // Clear the joblist. Make SURE to stop the job emitting "finished"
148  for( TQPtrListIterator<CachedImapJob> it( mJobList ); it.current(); ++it )
149  it.current()->setPassiveDestructor( true );
150  KMAccount::deleteFolderJobs();
151 
152  if ( disconnectSlave && mSlave ) {
153  TDEIO::Scheduler::disconnectSlave( mSlave );
154  mSlave = 0;
155  }
156  return folderList;
157 }
158 
159 //-----------------------------------------------------------------------------
160 void KMAcctCachedImap::cancelMailCheck()
161 {
162  // Make list of folders to reset, like in killAllJobs
163  TQValueList<KMFolderCachedImap*> folderList;
164  TQMap<TDEIO::Job*, jobData>::Iterator it = mapJobData.begin();
165  for (; it != mapJobData.end(); ++it) {
166  if ( (*it).cancellable && (*it).parent )
167  folderList << static_cast<KMFolderCachedImap*>((*it).parent->storage());
168  }
169  // Kill jobs
170  ImapAccountBase::cancelMailCheck();
171  // Reset sync states and emit folderComplete, this is important for
172  // KMAccount::checkingMail() to be reset, in case we restart checking mail later.
173  for( TQValueList<KMFolderCachedImap*>::Iterator it = folderList.begin(); it != folderList.end(); ++it ) {
174  KMFolderCachedImap *fld = *it;
175  fld->resetSyncState();
176  fld->setContentState(KMFolderCachedImap::imapNoInformation);
177  fld->setSubfolderState(KMFolderCachedImap::imapNoInformation);
178  fld->sendFolderComplete(false);
179  }
180 }
181 
182 // Reimplemented from ImapAccountBase because we only check one folder at a time
183 void KMAcctCachedImap::slotCheckQueuedFolders()
184 {
185  mMailCheckFolders.clear();
186  mMailCheckFolders.append( mFoldersQueuedForChecking.front() );
187  mFoldersQueuedForChecking.pop_front();
188  if ( mFoldersQueuedForChecking.isEmpty() )
189  disconnect( this, TQ_SIGNAL( finishedCheck( bool, CheckStatus ) ),
190  this, TQ_SLOT( slotCheckQueuedFolders() ) );
191 
192  kmkernel->acctMgr()->singleCheckMail(this, true);
193  mMailCheckFolders.clear();
194 }
195 
196 void KMAcctCachedImap::processNewMail( bool /*interactive*/ )
197 {
198  assert( mFolder ); // George says "better to crash then lose mail"
199 
200  if ( mMailCheckFolders.isEmpty() )
201  processNewMail( mFolder, true );
202  else {
203  KMFolder* f = mMailCheckFolders.front();
204  mMailCheckFolders.pop_front();
205 
206  // Only check mail if the folder really exists, it might have been removed by the sync in
207  // the meantime.
208  if ( f ) {
209  processNewMail( static_cast<KMFolderCachedImap *>( f->storage() ), !checkingSingleFolder() );
210  }
211  }
212 }
213 
214 void KMAcctCachedImap::processNewMail( KMFolderCachedImap* folder,
215  bool recurse )
216 {
217  assert( folder ); // George says "better to crash then lose mail"
218 
219  // This should never be set for a cached IMAP account
220  mAutoExpunge = false;
221  mCountLastUnread = 0;
222  mUnreadBeforeCheck.clear();
223  // stop sending noops during sync, that will keep the connection open
224  mNoopTimer.stop();
225 
226  // reset namespace todo
227  if ( folder == mFolder && !namespaces().isEmpty() ) {
228  TQStringList nsToList = namespaces()[PersonalNS];
229  TQStringList otherNSToCheck = namespaces()[OtherUsersNS];
230  otherNSToCheck += namespaces()[SharedNS];
231  for ( TQStringList::Iterator it = otherNSToCheck.begin();
232  it != otherNSToCheck.end(); ++it ) {
233  if ( (*it).isEmpty() ) {
234  // empty namespaces are included in the "normal" listing
235  // as the folders are created under the root folder
236  nsToList += *it;
237  }
238  }
239  folder->setNamespacesToList( nsToList );
240  }
241 
242  Q_ASSERT( !mMailCheckProgressItem );
243  mMailCheckProgressItem = KPIM::ProgressManager::createProgressItem(
244  "MailCheck" + TQString::number( id() ),
245  TQStyleSheet::escape( folder->label() ), // will be changed immediately in serverSync anyway
246  TQString(),
247  true, // can be cancelled
248  useSSL() || useTLS() );
249  connect( mMailCheckProgressItem, TQ_SIGNAL( progressItemCanceled( KPIM::ProgressItem* ) ),
250  this, TQ_SLOT( slotProgressItemCanceled( KPIM::ProgressItem* ) ) );
251 
252  folder->setAccount(this);
253  connect(folder, TQ_SIGNAL(folderComplete(KMFolderCachedImap*, bool)),
254  this, TQ_SLOT(postProcessNewMail(KMFolderCachedImap*, bool)));
255  folder->serverSync( recurse );
256 }
257 
258 void KMAcctCachedImap::postProcessNewMail( KMFolderCachedImap* folder, bool )
259 {
260  mNoopTimer.start( 60000 ); // send a noop every minute to avoid "connection broken" errors
261  disconnect(folder, TQ_SIGNAL(folderComplete(KMFolderCachedImap*, bool)),
262  this, TQ_SLOT(postProcessNewMail(KMFolderCachedImap*, bool)));
263  mMailCheckProgressItem->setComplete();
264  mMailCheckProgressItem = 0;
265 
266  if ( folder == mFolder ) {
267  // We remove everything from the deleted folders list after a full sync.
268  // Even if it fails (no permission), because on the next sync we want the folder to reappear,
269  // instead of the user being stuck with "can't delete" every time.
270  // And we do it for _all_ deleted folders, even those that were deleted on the server in the first place (slotListResult).
271  // Otherwise this might have side effects much later (e.g. when regaining permissions to a folder we could see before)
272 
273 #if 0 // this opens a race: delete a folder during a sync (after the sync checked that folder), and it'll be forgotten...
274  mDeletedFolders.clear();
275 #endif
276  mPreviouslyDeletedFolders.clear();
277  }
278 
279  KMail::ImapAccountBase::postProcessNewMail();
280 }
281 
282 void KMAcctCachedImap::addUnreadMsgCount( const KMFolderCachedImap *folder,
283  int countUnread )
284 {
285  if ( folder->imapPath() != "/INBOX/" ) {
286  // new mail in INBOX is processed with KMAccount::processNewMsg() and
287  // therefore doesn't need to be counted here
288  const TQString folderId = folder->folder()->idString();
289  int newInFolder = countUnread;
290  if ( mUnreadBeforeCheck.find( folderId ) != mUnreadBeforeCheck.end() )
291  newInFolder -= mUnreadBeforeCheck[folderId];
292  if ( newInFolder > 0 )
293  addToNewInFolder( folderId, newInFolder );
294  }
295  mCountUnread += countUnread;
296 }
297 
298 void KMAcctCachedImap::addLastUnreadMsgCount( const KMFolderCachedImap *folder,
299  int countLastUnread )
300 {
301  mUnreadBeforeCheck[folder->folder()->idString()] = countLastUnread;
302  mCountLastUnread += countLastUnread;
303 }
304 
305 //
306 //
307 // read/write config
308 //
309 //
310 
311 void KMAcctCachedImap::readConfig( /*const*/ TDEConfig/*Base*/ & config ) {
312  ImapAccountBase::readConfig( config );
313  // Apparently this method is only ever called once (from KMKernel::init) so this is ok
314  mPreviouslyDeletedFolders = config.readListEntry( "deleted-folders" );
315  mDeletedFolders.clear(); // but just in case...
316  const TQStringList oldPaths = config.readListEntry( "renamed-folders-paths" );
317  const TQStringList newNames = config.readListEntry( "renamed-folders-names" );
318  TQStringList::const_iterator it = oldPaths.begin();
319  TQStringList::const_iterator nameit = newNames.begin();
320  for( ; it != oldPaths.end() && nameit != newNames.end(); ++it, ++nameit ) {
321  addRenamedFolder( *it, TQString(), *nameit );
322  }
323  mGroupwareType = (GroupwareType)config.readNumEntry( "groupwareType", GroupwareKolab );
324 }
325 
326 void KMAcctCachedImap::writeConfig( TDEConfig/*Base*/ & config ) /*const*/ {
327  ImapAccountBase::writeConfig( config );
328  config.writeEntry( "deleted-folders", mDeletedFolders + mPreviouslyDeletedFolders );
329  config.writeEntry( "renamed-folders-paths", mRenamedFolders.keys() );
330  const TQValueList<RenamedFolder> values = mRenamedFolders.values();
331  TQStringList lstNames;
332  TQValueList<RenamedFolder>::const_iterator it = values.begin();
333  for ( ; it != values.end() ; ++it )
334  lstNames.append( (*it).mNewName );
335  config.writeEntry( "renamed-folders-names", lstNames );
336  config.writeEntry( "groupwareType", mGroupwareType );
337 }
338 
339 void KMAcctCachedImap::invalidateIMAPFolders()
340 {
341  invalidateIMAPFolders( mFolder );
342 }
343 
344 void KMAcctCachedImap::invalidateIMAPFolders( KMFolderCachedImap* folder )
345 {
346  if( !folder || !folder->folder() )
347  return;
348 
349  folder->setAccount(this);
350 
351  TQStringList strList;
352  TQValueList<TQGuardedPtr<KMFolder> > folderList;
353  kmkernel->dimapFolderMgr()->createFolderList( &strList, &folderList,
354  folder->folder()->child(), TQString(),
355  false );
356  TQValueList<TQGuardedPtr<KMFolder> >::Iterator it;
357  mCountLastUnread = 0;
358  mUnreadBeforeCheck.clear();
359 
360  for( it = folderList.begin(); it != folderList.end(); ++it ) {
361  KMFolder *f = *it;
362  if( f && f->folderType() == KMFolderTypeCachedImap ) {
363  KMFolderCachedImap *cfolder = static_cast<KMFolderCachedImap*>(f->storage());
364  // This invalidates the folder completely
365  cfolder->setUidValidity("INVALID");
366  cfolder->writeUidCache();
367  }
368  }
369  folder->setUidValidity("INVALID");
370  folder->writeUidCache();
371 
372  processNewMailInFolder( folder->folder(), Recursive );
373 }
374 
375 //-----------------------------------------------------------------------------
376 void KMAcctCachedImap::addDeletedFolder( KMFolder* folder )
377 {
378  if ( !folder || folder->folderType() != KMFolderTypeCachedImap )
379  return;
380  KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(folder->storage());
381  addDeletedFolder( storage->imapPath() );
382  kdDebug(5006) << k_funcinfo << storage->imapPath() << endl;
383 
384  // Add all child folders too
385  if( folder->child() ) {
386  KMFolderNode *node = folder->child()->first();
387  while( node ) {
388  if( !node->isDir() ) {
389  addDeletedFolder( static_cast<KMFolder*>( node ) ); // recurse
390  }
391  node = folder->child()->next();
392  }
393  }
394 }
395 
396 void KMAcctCachedImap::addDeletedFolder( const TQString& imapPath )
397 {
398  mDeletedFolders << imapPath;
399 }
400 
401 TQStringList KMAcctCachedImap::deletedFolderPaths( const TQString& subFolderPath ) const
402 {
403  TQStringList lst;
404  for ( TQStringList::const_iterator it = mDeletedFolders.begin(); it != mDeletedFolders.end(); ++it ) {
405  if ( (*it).startsWith( subFolderPath ) )
406  // We must reverse the order, so that sub sub sub folders are deleted first
407  lst.prepend( *it );
408  }
409  for ( TQStringList::const_iterator it = mPreviouslyDeletedFolders.begin(); it != mPreviouslyDeletedFolders.end(); ++it ) {
410  if ( (*it).startsWith( subFolderPath ) )
411  lst.prepend( *it );
412  }
413  kdDebug(5006) << "KMAcctCachedImap::deletedFolderPaths for " << subFolderPath << " returning: " << lst << endl;
414  Q_ASSERT( !lst.isEmpty() );
415  return lst;
416 }
417 
418 bool KMAcctCachedImap::isDeletedFolder( const TQString& subFolderPath ) const
419 {
420  return mDeletedFolders.find( subFolderPath ) != mDeletedFolders.end();
421 }
422 
423 bool KMAcctCachedImap::isPreviouslyDeletedFolder( const TQString& subFolderPath ) const
424 {
425  return mPreviouslyDeletedFolders.find( subFolderPath ) != mPreviouslyDeletedFolders.end();
426 }
427 
428 void KMAcctCachedImap::removeDeletedFolder( const TQString& subFolderPath )
429 {
430  mDeletedFolders.remove( subFolderPath );
431  mPreviouslyDeletedFolders.remove( subFolderPath );
432 }
433 
434 void KMAcctCachedImap::addRenamedFolder( const TQString& subFolderPath, const TQString& oldLabel, const TQString& newName )
435 {
436  mRenamedFolders.insert( subFolderPath, RenamedFolder( oldLabel, newName ) );
437 }
438 
439 void KMAcctCachedImap::removeRenamedFolder( const TQString& subFolderPath )
440 {
441  mRenamedFolders.remove( subFolderPath );
442 }
443 
444 void KMAcctCachedImap::slotProgressItemCanceled( ProgressItem* )
445 {
446  bool abortConnection = !mSlaveConnected;
447  killAllJobs( abortConnection );
448  if ( abortConnection ) {
449  // If we were trying to connect, tell kmfoldercachedimap so that it moves on
450  emit connectionResult( TDEIO::ERR_USER_CANCELED, TQString() );
451  }
452 }
453 
454 FolderStorage* KMAcctCachedImap::rootFolder() const
455 {
456  return mFolder;
457 }
458 
459 
460 TQString KMAcctCachedImap::renamedFolder( const TQString& imapPath ) const
461 {
462  TQMap<TQString, RenamedFolder>::ConstIterator renit = mRenamedFolders.find( imapPath );
463  if ( renit != mRenamedFolders.end() )
464  return (*renit).mNewName;
465  return TQString();
466 }
467 
468 #include "kmacctcachedimap.moc"
virtual int find(const KMMsgBase *msg) const =0
Returns the index of the given message or -1 if not found.
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.
Definition: kmfolder.h:157
The FolderStorage class is the bass class for the storage related aspects of a collection of mail (a ...
Definition: folderstorage.h:79
void invalidateIMAPFolders()
Delete all IMAP folders and resync them.
The account manager is responsible for creating accounts of various types via the factory method crea...
KMFolderType folderType() const
Returns the type of this folder.
Definition: kmfolder.cpp:233
folderdiaquotatab.h
Definition: aboutdata.cpp:40
Mail folder.
Definition: kmfolder.h:68