kmail

kmfoldercachedimap.cpp
1
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36#include <errno.h>
37
38#include <tqvaluevector.h>
39
40#include "kmkernel.h"
41#include "kmfoldercachedimap.h"
42#include "undostack.h"
43#include "kmfoldermgr.h"
44#include "kmacctcachedimap.h"
45#include "accountmanager.h"
47#include "kmailicalifaceimpl.h"
48#include "kmfolder.h"
49#include "kmglobal.h"
50#include "acljobs.h"
51#include "broadcaststatus.h"
52using KPIM::BroadcastStatus;
53#include "progressmanager.h"
54
55using KMail::CachedImapJob;
56#include "imapaccountbase.h"
57using KMail::ImapAccountBase;
58#include "listjob.h"
59using KMail::ListJob;
60
61#include "kmfolderseldlg.h"
62#include "kmcommands.h"
63#include "kmmainwidget.h"
64
65#include <tdeapplication.h>
66#include <tdemessagebox.h>
67#include <tdelocale.h>
68#include <kdebug.h>
69#include <tdeconfig.h>
70#include <tdeio/global.h>
71#include <tdeio/scheduler.h>
72#include <tqbuffer.h>
73#include <tqbuttongroup.h>
74#include <tqcombobox.h>
75#include <tqfile.h>
76#include <tqhbox.h>
77#include <tqlabel.h>
78#include <tqlayout.h>
79#include <tqradiobutton.h>
80#include <tqvaluelist.h>
81#include "annotationjobs.h"
82#include "quotajobs.h"
83using namespace KMail;
84#include <globalsettings.h>
85
86#define UIDCACHE_VERSION 1
87#define MAIL_LOSS_DEBUGGING 0
88
89static TQString incidencesForToString( KMFolderCachedImap::IncidencesFor r ) {
90 switch (r) {
91 case KMFolderCachedImap::IncForNobody: return "nobody";
92 case KMFolderCachedImap::IncForAdmins: return "admins";
93 case KMFolderCachedImap::IncForReaders: return "readers";
94 }
95 return TQString(); // can't happen
96}
97
98static KMFolderCachedImap::IncidencesFor incidencesForFromString( const TQString& str ) {
99 if ( str == "nobody" ) return KMFolderCachedImap::IncForNobody;
100 if ( str == "admins" ) return KMFolderCachedImap::IncForAdmins;
101 if ( str == "readers" ) return KMFolderCachedImap::IncForReaders;
102 return KMFolderCachedImap::IncForAdmins; // by default
103}
104
105DImapTroubleShootDialog::DImapTroubleShootDialog( TQWidget* parent,
106 const char* name )
107 : KDialogBase( Plain, i18n( "Troubleshooting IMAP Cache" ),
108 Ok | Cancel, Cancel, parent, name, true ),
109 rc( None )
110{
111 TQFrame* page = plainPage();
112 TQVBoxLayout *topLayout = new TQVBoxLayout( page, 0 );
113 // spell "lose" correctly. but don't cause a fuzzy.
114 TQString txt = i18n( "<p><b>Troubleshooting the IMAP cache.</b></p>"
115 "<p>If you have problems with synchronizing an IMAP "
116 "folder, you should first try rebuilding the index "
117 "file. This will take some time to rebuild, but will "
118 "not cause any problems.</p><p>If that is not enough, "
119 "you can try refreshing the IMAP cache. If you do this, "
120 "you will loose all your local changes for this folder "
121 "and all its subfolders.</p>",
122 "<p><b>Troubleshooting the IMAP cache.</b></p>"
123 "<p>If you have problems with synchronizing an IMAP "
124 "folder, you should first try rebuilding the index "
125 "file. This will take some time to rebuild, but will "
126 "not cause any problems.</p><p>If that is not enough, "
127 "you can try refreshing the IMAP cache. If you do this, "
128 "you will lose all your local changes for this folder "
129 "and all its subfolders.</p>" );
130 topLayout->addWidget( new TQLabel( txt, page ) );
131
132 mButtonGroup = new TQButtonGroup( 0 );
133
134 mIndexButton = new TQRadioButton( page );
135 mIndexButton->setText( i18n( "Rebuild &Index" ) );
136 mButtonGroup->insert( mIndexButton );
137 topLayout->addWidget( mIndexButton );
138
139 TQHBox *hbox = new TQHBox( page );
140 TQLabel *scopeLabel = new TQLabel( i18n( "Scope:" ), hbox );
141 scopeLabel->setEnabled( false );
142 mIndexScope = new TQComboBox( hbox );
143 mIndexScope->insertItem( i18n( "Only current folder" ) );
144 mIndexScope->insertItem( i18n( "Current folder and all subfolders" ) );
145 mIndexScope->insertItem( i18n( "All folders of this account" ) );
146 mIndexScope->setEnabled( false );
147 topLayout->addWidget( hbox );
148
149 mCacheButton = new TQRadioButton( page );
150 mCacheButton->setText( i18n( "Refresh &Cache" ) );
151 mButtonGroup->insert( mCacheButton );
152 topLayout->addWidget( mCacheButton );
153
154 enableButtonSeparator( true );
155
156 connect ( mIndexButton, TQ_SIGNAL(toggled(bool)), mIndexScope, TQ_SLOT(setEnabled(bool)) );
157 connect ( mIndexButton, TQ_SIGNAL(toggled(bool)), scopeLabel, TQ_SLOT(setEnabled(bool)) );
158 connect( mButtonGroup, TQ_SIGNAL( clicked( int ) ), TQ_SLOT( slotChanged() ) );
159 connect( this, TQ_SIGNAL( okClicked () ), this, TQ_SLOT( slotDone() ) );
160 enableButtonOK( false );
161}
162
163int DImapTroubleShootDialog::run()
164{
165 DImapTroubleShootDialog d;
166 d.exec();
167 return d.rc;
168}
169
170void DImapTroubleShootDialog::slotChanged()
171{
172 enableButtonOK( mButtonGroup->selected() != 0 );
173}
174
175void DImapTroubleShootDialog::slotDone()
176{
177 rc = None;
178 if ( mIndexButton->isOn() )
179 rc = mIndexScope->currentItem();
180 else if ( mCacheButton->isOn() )
181 rc = RefreshCache;
182 done( Ok );
183}
184
185KMFolderCachedImap::KMFolderCachedImap( KMFolder* folder, const char* aName )
186 : KMFolderMaildir( folder, aName ),
187 mSyncState( SYNC_STATE_INITIAL ), mContentState( imapNoInformation ),
188 mSubfolderState( imapNoInformation ),
189 mIncidencesFor( IncForAdmins ),
190 mSharedSeenFlags( false ),
191 mIsSelected( false ),
192 mCheckFlags( true ), mReadOnly( false ), mAccount( NULL ), uidMapDirty( true ),
193 uidWriteTimer( -1 ), mLastUid( 0 ), mTentativeHighestUid( 0 ),
194 mFoundAnIMAPDigest( false ),
195 mUserRights( 0 ), mOldUserRights( 0 ), mUserRightsState( KMail::ACLJobs::NotFetchedYet ),
196 mACLListState( KMail::ACLJobs::NotFetchedYet ),
197 mSilentUpload( false ),
198 /*mHoldSyncs( false ),*/
199 mFolderRemoved( false ),
200 mRecurse( true ),
201 mQuotaOnly( false ),
202 mAnnotationFolderTypeChanged( false ),
203 mIncidencesForChanged( false ),
204 mSharedSeenFlagsChanged( false ),
205 mStatusChangedLocally( false ),
206 mPersonalNamespacesCheckDone( true ),
207 mQuotaInfo(), mSomeSubFolderCloseToQuotaChanged( false ), mAlarmsBlocked( false ),
208 mRescueCommandCount( 0 ),
209 mPermanentFlags( 31 ) // assume standard flags by default (see imap4/imapinfo.h for bit fields values)
210{
211 setUidValidity("");
212 // if we fail to read a uid file but there is one, nuke it
213 if ( readUidCache() == -1 ) {
214 if ( TQFile::exists( uidCacheLocation() ) ) {
215 KMessageBox::error( 0,
216 i18n( "The UID cache file for folder %1 could not be read. There "
217 "could be a problem with file system permission, or it is corrupted."
218 ).arg( folder->prettyURL() ) );
219 // try to unlink it, in case it was corruped. If it couldn't be read
220 // because of permissions, this will fail, which is fine
221 unlink( TQFile::encodeName( uidCacheLocation() ) );
222 }
223 }
224
225 mProgress = 0;
226}
227
228KMFolderCachedImap::~KMFolderCachedImap()
229{
230 if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
231 writeConfig();
232}
233
234void KMFolderCachedImap::reallyDoClose( const char* owner )
235{
236 if( !mFolderRemoved ) {
237 writeUidCache();
238 }
239 KMFolderMaildir::reallyDoClose( owner );
240}
241
242void KMFolderCachedImap::initializeFrom( KMFolderCachedImap* parent )
243{
244 setAccount( parent->account() );
245 // Now that we have an account, tell it that this folder was created:
246 // if this folder was just removed, then we don't really want to remove it from the server.
247 mAccount->removeDeletedFolder( imapPath() );
248 setUserRights( parent->userRights(), parent->userRightsState() );
249}
250
251void KMFolderCachedImap::readConfig()
252{
253 TDEConfig* config = KMKernel::config();
254 TDEConfigGroupSaver saver( config, "Folder-" + folder()->idString() );
255 if( mImapPath.isEmpty() ) mImapPath = config->readEntry( "ImapPath" );
256 if( TQString( name() ).upper() == "INBOX" && mImapPath == "/INBOX/" )
257 {
258 folder()->setLabel( i18n( "inbox" ) );
259 // for the icon
260 folder()->setSystemFolder( true );
261 }
262 mNoContent = config->readBoolEntry( "NoContent", false );
263 mReadOnly = config->readBoolEntry( "ReadOnly", false );
264 if ( !config->readEntry( "FolderAttributes" ).isEmpty() )
265 mFolderAttributes = config->readEntry( "FolderAttributes" );
266
267 if ( mAnnotationFolderType != "FROMSERVER" ) {
268 mAnnotationFolderType = config->readEntry( "Annotation-FolderType" );
269 // if there is an annotation, it has to be XML
270 if ( !mAnnotationFolderType.isEmpty() && !mAnnotationFolderType.startsWith( "mail" ) )
271 kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
272// kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
273// << " readConfig: mAnnotationFolderType=" << mAnnotationFolderType << endl;
274 }
275 mIncidencesFor = incidencesForFromString( config->readEntry( "IncidencesFor" ) );
276 mAlarmsBlocked = config->readBoolEntry( "AlarmsBlocked", false );
277// kdDebug(5006) << ( mImapPath.isEmpty() ? label() : mImapPath )
278// << " readConfig: mIncidencesFor=" << mIncidencesFor << endl;
279 mSharedSeenFlags = config->readBoolEntry( "SharedSeenFlags", false );
280
281 mUserRights = config->readNumEntry( "UserRights", 0 );
282 mUserRightsState = static_cast<KMail::ACLJobs::ACLFetchState>(
283 config->readNumEntry( "UserRightsState", KMail::ACLJobs::NotFetchedYet ) );
284 mOldUserRights = mUserRights;
285
286 int storageQuotaUsage = config->readNumEntry( "StorageQuotaUsage", -1 );
287 int storageQuotaLimit = config->readNumEntry( "StorageQuotaLimit", -1 );
288 TQString storageQuotaRoot = config->readEntry( "StorageQuotaRoot", TQString() );
289 if ( !storageQuotaRoot.isNull() ) { // isEmpty() means we know there is no quota set
290 mQuotaInfo.setName( "STORAGE" );
291 mQuotaInfo.setRoot( storageQuotaRoot );
292
293 if ( storageQuotaUsage > -1 )
294 mQuotaInfo.setCurrent( storageQuotaUsage );
295 if ( storageQuotaLimit > -1 )
296 mQuotaInfo.setMax( storageQuotaLimit );
297 }
298
300
301 mStatusChangedLocally =
302 config->readBoolEntry( "StatusChangedLocally", false );
303 TQStringList uidsChanged = config->readListEntry( "UIDStatusChangedLocally" );
304 for ( TQStringList::iterator it = uidsChanged.begin(); it != uidsChanged.end(); it++ ) {
305 mUIDsOfLocallyChangedStatuses.insert( ( *it ).toUInt() );
306 }
307
308 mAnnotationFolderTypeChanged = config->readBoolEntry( "AnnotationFolderTypeChanged", false );
309 mIncidencesForChanged = config->readBoolEntry( "IncidencesForChanged", false );
310 mSharedSeenFlagsChanged = config->readBoolEntry( "SharedSeenFlagsChanged", false );
311
312 if ( mImapPath.isEmpty() ) {
313 mImapPathCreation = config->readEntry("ImapPathCreation");
314 }
315
316 TQStringList delUids = config->readListEntry( "UIDSDeletedSinceLastSync" );
317#if MAIL_LOSS_DEBUGGING
318 kdDebug( 5006 ) << "READING IN UIDSDeletedSinceLastSync: " << folder()->prettyURL() << endl << uids << endl;
319#endif
320 for ( TQStringList::iterator it = delUids.begin(); it != delUids.end(); it++ ) {
321 mDeletedUIDsSinceLastSync.insert( (*it).toULong(), 0);
322 }
323}
324
325void KMFolderCachedImap::writeConfig()
326{
327 // don't re-write the config of a removed folder, this has just been deleted in
328 // the folder manager
329 if ( mFolderRemoved )
330 return;
331
332 TDEConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
333 configGroup.writeEntry( "ImapPath", mImapPath );
334 configGroup.writeEntry( "NoContent", mNoContent );
335 configGroup.writeEntry( "ReadOnly", mReadOnly );
336 configGroup.writeEntry( "FolderAttributes", mFolderAttributes );
337
338 // StatusChangedLocally is always false, as we use UIDStatusChangedLocally now
339 configGroup.writeEntry( "StatusChangedLocally", false );
340 TQStringList uidsToWrite;
341 for( std::set<ulong>::iterator it = mUIDsOfLocallyChangedStatuses.begin();
342 it != mUIDsOfLocallyChangedStatuses.end(); it++ ) {
343 uidsToWrite.append( TQString::number( (*it) ) );
344 }
345 configGroup.writeEntry( "UIDStatusChangedLocally", uidsToWrite );
346 if ( !mImapPathCreation.isEmpty() ) {
347 if ( mImapPath.isEmpty() ) {
348 configGroup.writeEntry( "ImapPathCreation", mImapPathCreation );
349 } else {
350 configGroup.deleteEntry( "ImapPathCreation" );
351 }
352 }
353 if ( !mDeletedUIDsSinceLastSync.isEmpty() ) {
354 TQValueList<ulong> uids = mDeletedUIDsSinceLastSync.keys();
355 TQStringList uidstrings;
356 for( TQValueList<ulong>::iterator it = uids.begin(); it != uids.end(); it++ ) {
357 uidstrings.append( TQString::number( (*it) ) );
358 }
359 configGroup.writeEntry( "UIDSDeletedSinceLastSync", uidstrings );
360#if MAIL_LOSS_DEBUGGING
361 kdDebug( 5006 ) << "WRITING OUT UIDSDeletedSinceLastSync in: " << folder( )->prettyURL( ) << endl << uidstrings << endl;
362#endif
363 } else {
364 configGroup.deleteEntry( "UIDSDeletedSinceLastSync" );
365 }
366 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
368}
369
370void KMFolderCachedImap::writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig()
371{
372 TDEConfigGroup configGroup( KMKernel::config(), "Folder-" + folder()->idString() );
373 if ( !folder()->noContent() )
374 {
375 configGroup.writeEntry( "AnnotationFolderTypeChanged", mAnnotationFolderTypeChanged );
376 configGroup.writeEntry( "Annotation-FolderType", mAnnotationFolderType );
377 configGroup.writeEntry( "IncidencesForChanged", mIncidencesForChanged );
378 configGroup.writeEntry( "IncidencesFor", incidencesForToString( mIncidencesFor ) );
379 configGroup.writeEntry( "AlarmsBlocked", mAlarmsBlocked );
380 configGroup.writeEntry( "SharedSeenFlags", mSharedSeenFlags );
381 configGroup.writeEntry( "SharedSeenFlagsChanged", mSharedSeenFlagsChanged );
382 if ( mUserRightsState != KMail::ACLJobs::FetchFailed ) { // No point in overwriting valid results with invalid ones
383 configGroup.writeEntry( "UserRights", mUserRights );
384 configGroup.writeEntry( "UserRightsState", mUserRightsState );
385 }
386
387 configGroup.deleteEntry( "StorageQuotaUsage");
388 configGroup.deleteEntry( "StorageQuotaRoot");
389 configGroup.deleteEntry( "StorageQuotaLimit");
390
391 if ( mQuotaInfo.isValid() ) {
392 if ( mQuotaInfo.current().isValid() ) {
393 configGroup.writeEntry( "StorageQuotaUsage", mQuotaInfo.current().toInt() );
394 }
395 if ( mQuotaInfo.max().isValid() ) {
396 configGroup.writeEntry( "StorageQuotaLimit", mQuotaInfo.max().toInt() );
397 }
398 configGroup.writeEntry( "StorageQuotaRoot", mQuotaInfo.root() );
399 }
400 }
401}
402
403int KMFolderCachedImap::create()
404{
405 int rc = KMFolderMaildir::create();
406 // FIXME why the below? - till
407 readConfig();
408 mUnreadMsgs = -1;
409 return rc;
410}
411
412void KMFolderCachedImap::remove()
413{
414 mFolderRemoved = true;
415
416 TQString part1 = folder()->path() + "/." + dotEscape(name());
417 TQString uidCacheFile = part1 + ".uidcache";
418 // This is the account folder of an account that was just removed
419 // When this happens, be sure to delete all traces of the cache
420 if( TQFile::exists(uidCacheFile) )
421 unlink( TQFile::encodeName( uidCacheFile ) );
422
424}
425
426TQString KMFolderCachedImap::uidCacheLocation() const
427{
428 TQString sLocation(folder()->path());
429 if (!sLocation.isEmpty()) sLocation += '/';
430 return sLocation + '.' + dotEscape(fileName()) + ".uidcache";
431}
432
433int KMFolderCachedImap::readUidCache()
434{
435 TQFile uidcache( uidCacheLocation() );
436 if( uidcache.open( IO_ReadOnly ) ) {
437 char buf[1024];
438 int len = uidcache.readLine( buf, sizeof(buf) );
439 if( len > 0 ) {
440 int cacheVersion;
441 sscanf( buf, "# KMail-UidCache V%d\n", &cacheVersion );
442 if( cacheVersion == UIDCACHE_VERSION ) {
443 len = uidcache.readLine( buf, sizeof(buf) );
444 if( len > 0 ) {
445 setUidValidity( TQString(TQString::fromLocal8Bit(buf)).stripWhiteSpace() );
446 len = uidcache.readLine( buf, sizeof(buf) );
447 if( len > 0 ) {
448#if MAIL_LOSS_DEBUGGING
449 kdDebug(5006) << "Reading in last uid from cache: " << TQString::fromLocal8Bit(buf).stripWhiteSpace() << " in " << folder()->prettyURL() << endl;
450#endif
451 // load the last known highest uid from the on disk cache
452 setLastUid( TQString(TQString::fromLocal8Bit(buf)).stripWhiteSpace().toULong() );
453 return 0;
454 }
455 }
456 }
457 }
458 }
459 return -1;
460}
461
462int KMFolderCachedImap::writeUidCache()
463{
464 if( uidValidity().isEmpty() || uidValidity() == "INVALID" ) {
465 // No info from the server yet, remove the file.
466 if( TQFile::exists( uidCacheLocation() ) )
467 return unlink( TQFile::encodeName( uidCacheLocation() ) );
468 return 0;
469 }
470#if MAIL_LOSS_DEBUGGING
471 kdDebug(5006) << "Writing out UID cache lastuid: " << lastUid() << " in: " << folder()->prettyURL() << endl;
472#endif
473 TQFile uidcache( uidCacheLocation() );
474 if( uidcache.open( IO_WriteOnly ) ) {
475 TQTextStream str( &uidcache );
476 str << "# KMail-UidCache V" << UIDCACHE_VERSION << endl;
477 str << uidValidity() << endl;
478 str << lastUid() << endl;
479 uidcache.flush();
480 if ( uidcache.status() == IO_Ok ) {
481 // fsync( uidcache.handle() ); /* this is probably overkill */
482 uidcache.close();
483 if ( uidcache.status() == IO_Ok )
484 return 0;
485 }
486 }
487 KMessageBox::error( 0,
488 i18n( "The UID cache file for folder %1 could not be written. There "
489 "could be a problem with file system permission." ).arg( folder()->prettyURL() ) );
490
491 return -1;
492}
493
494void KMFolderCachedImap::reloadUidMap()
495{
496 //kdDebug(5006) << "Reloading Uid Map " << endl;
497 uidMap.clear();
498 open("reloadUdi");
499 for( int i = 0; i < count(); ++i ) {
500 KMMsgBase *msg = getMsgBase( i );
501 if( !msg ) continue;
502 ulong uid = msg->UID();
503 //kdDebug(5006) << "Inserting: " << i << " with uid: " << uid << endl;
504 uidMap.insert( uid, i );
505 }
506 close("reloadUdi");
507 uidMapDirty = false;
508}
509
510KMMessage* KMFolderCachedImap::take(int idx)
511{
512 uidMapDirty = true;
513 rememberDeletion( idx );
514 return KMFolderMaildir::take(idx);
515}
516
517void KMFolderCachedImap::takeTemporarily( int idx )
518{
519 KMFolderMaildir::take( idx );
520}
521
522// Add a message without clearing it's X-UID field.
523int KMFolderCachedImap::addMsgInternal( KMMessage* msg, bool newMail,
524 int* index_return )
525{
526 // Possible optimization: Only dirty if not filtered below
527 ulong uid = msg->UID();
528 if( uid != 0 ) {
529 uidMapDirty = true;
530 }
531
532 KMFolderOpener openThis(folder(), "KMFolderCachedImap::addMsgInternal");
533 int rc = openThis.openResult();
534 if ( rc ) {
535 kdDebug(5006) << k_funcinfo << "open: " << rc << " of folder: " << label() << endl;
536 return rc;
537 }
538
539 // Add the message
540 rc = KMFolderMaildir::addMsg(msg, index_return);
541
542 if( newMail && ( imapPath() == "/INBOX/" ||
543 ( ( mUserRights != ACLJobs::Ok || userRights() & ACLJobs::Administer)
544 && (contentsType() == ContentsTypeMail || GlobalSettings::self()->filterGroupwareFolders()) ) ) )
545 {
546 // This is a new message. Filter it - maybe
547 bool filter = false;
548 if ( GlobalSettings::filterSourceFolders().isEmpty() ) {
549 if ( imapPath() == "/INBOX/" )
550 filter = true;
551 } else {
552 if ( GlobalSettings::filterSourceFolders().contains( folder()->id() ) )
553 filter = true;
554 }
555 if ( filter )
556 mAccount->processNewMsg( msg );
557 }
558
559 return rc;
560}
561
562/* Reimplemented from KMFolderMaildir */
563int KMFolderCachedImap::addMsg(KMMessage* msg, int* index_return)
564{
565 if ( !canAddMsgNow( msg, index_return ) ) return 0;
566 // Add it to storage
567 int rc = KMFolderMaildir::addMsgInternal(msg, index_return, true /*stripUID*/);
568 return rc;
569}
570
571void KMFolderCachedImap::rememberDeletion( int idx )
572{
573 KMMsgBase *msg = getMsgBase( idx );
574 assert(msg);
575 long uid = msg->UID();
576 assert(uid>=0);
577 mDeletedUIDsSinceLastSync.insert(uid, 0);
578 kdDebug(5006) << "Explicit delete of UID " << uid << " at index: " << idx << " in " << folder()->prettyURL() << endl;
579}
580
581/* Reimplemented from KMFolderMaildir */
582void KMFolderCachedImap::removeMsg(int idx, bool imapQuiet)
583{
584 if ( contentsType() != ContentsTypeMail ) {
585 kdDebug(5006) << k_funcinfo << "Deleting message with idx " << idx << " in folder " << label() << endl;
586 }
587 uidMapDirty = true;
588 rememberDeletion( idx );
589 // Remove it from disk
590 KMFolderMaildir::removeMsg(idx,imapQuiet);
591}
592
593bool KMFolderCachedImap::canRemoveFolder() const {
594 // If this has subfolders it can't be removed
595 if( folder() && folder()->child() && folder()->child()->count() > 0 )
596 return false;
597
598#if 0
599 // No special condition here, so let base class decide
600 return KMFolderMaildir::canRemoveFolder();
601#endif
602 return true;
603}
604
605/* Reimplemented from KMFolderDir */
606int KMFolderCachedImap::rename( const TQString& aName,
607 KMFolderDir* /*aParent*/ )
608{
609 if ( account() == 0 || imapPath().isEmpty() ) {
610 // This can happen when creating a folder and then renaming it without syncing before,
611 // see https://issues.kolab.org/issue3658
612 TQString err = i18n("You must synchronize with the server before renaming IMAP folders.");
613 KMessageBox::error( 0, err );
614 return -1;
615 }
616
617 TQString oldName = mAccount->renamedFolder( imapPath() );
618 if ( oldName.isEmpty() ) oldName = name();
619 if ( aName == oldName )
620 // Stupid user trying to rename it to it's old name :)
621 return 0;
622
623 // Make the change appear to the user with setLabel, but we'll do the change
624 // on the server during the next sync. The name() is the name at the time of
625 // the last sync. Only rename if the new one is different. If it's the same,
626 // don't rename, but also make sure the rename is reset, in the case of
627 // A -> B -> A renames.
628 if ( name() != aName )
629 mAccount->addRenamedFolder( imapPath(), folder()->label(), aName );
630 else
631 mAccount->removeRenamedFolder( imapPath() );
632
633 folder()->setLabel( aName );
634 emit nameChanged(); // for kmailicalifaceimpl
635
636 return 0;
637}
638
639KMFolder* KMFolderCachedImap::trashFolder() const
640{
641 TQString trashStr = account()->trash();
642 return kmkernel->dimapFolderMgr()->findIdString( trashStr );
643}
644
645void KMFolderCachedImap::setLastUid( ulong uid )
646{
647#if MAIL_LOSS_DEBUGGING
648 kdDebug(5006) << "Setting mLastUid to: " << uid << " in " << folder()->prettyURL() << endl;
649#endif
650 mLastUid = uid;
651 if( uidWriteTimer == -1 )
652 // Write in one minute
653 uidWriteTimer = startTimer( 60000 );
654}
655
656void KMFolderCachedImap::timerEvent( TQTimerEvent* )
657{
658 killTimer( uidWriteTimer );
659 uidWriteTimer = -1;
660 if ( writeUidCache() == -1 )
661 unlink( TQFile::encodeName( uidCacheLocation() ) );
662}
663
664ulong KMFolderCachedImap::lastUid()
665{
666 return mLastUid;
667}
668
669KMMsgBase* KMFolderCachedImap::findByUID( ulong uid )
670{
671 bool mapReloaded = false;
672 if( uidMapDirty ) {
673 reloadUidMap();
674 mapReloaded = true;
675 }
676
677 TQMap<ulong,int>::Iterator it = uidMap.find( uid );
678 if( it != uidMap.end() ) {
679 KMMsgBase *msg = getMsgBase( *it );
680#if MAIL_LOSS_DEBUGGING
681 kdDebug(5006) << "Folder: " << folder()->prettyURL() << endl;
682 kdDebug(5006) << "UID " << uid << " is supposed to be in the map" << endl;
683 kdDebug(5006) << "UID's index is to be " << *it << endl;
684 kdDebug(5006) << "There is a message there? " << (msg != 0) << endl;
685 if ( msg ) {
686 kdDebug(5006) << "Its UID is: " << msg->UID() << endl;
687 }
688#endif
689
690 if( msg && msg->UID() == uid )
691 return msg;
692 kdDebug(5006) << "########## Didn't find uid: " << uid << "in cache athough it's supposed to be there!" << endl;
693 } else {
694#if MAIL_LOSS_DEBUGGING
695 kdDebug(5006) << "Didn't find uid: " << uid << "in cache!" << endl;
696#endif
697 }
698 // Not found by now
699 // if( mapReloaded )
700 // Not here then
701 return 0;
702 // There could be a problem in the maps. Rebuild them and try again
703 reloadUidMap();
704 it = uidMap.find( uid );
705 if( it != uidMap.end() )
706 // Since the uid map is just rebuilt, no need for the sanity check
707 return getMsgBase( *it );
708#if MAIL_LOSS_DEBUGGING
709 else
710 kdDebug(5006) << "Reloaded, but stil didn't find uid: " << uid << endl;
711#endif
712 // Then it's not here
713 return 0;
714}
715
716// This finds and sets the proper account for this folder if it has
717// not been done
718KMAcctCachedImap *KMFolderCachedImap::account() const
719{
720 if( (KMAcctCachedImap *)mAccount == 0 && kmkernel && kmkernel->acctMgr() ) {
721 // Find the account
722 mAccount = static_cast<KMAcctCachedImap *>( kmkernel->acctMgr()->findByName( name() ) );
723 }
724
725 return mAccount;
726}
727
728void KMFolderCachedImap::slotTroubleshoot()
729{
730 const int rc = DImapTroubleShootDialog::run();
731
732 if( rc == DImapTroubleShootDialog::RefreshCache ) {
733 // Refresh cache
734 if( !account() ) {
735 KMessageBox::sorry( 0, i18n("No account setup for this folder.\n"
736 "Please try running a sync before this.") );
737 return;
738 }
739 TQString str = i18n("Are you sure you want to refresh the IMAP cache of "
740 "the folder %1 and all its subfolders?\nThis will "
741 "remove all changes you have done locally to your "
742 "folders.").arg( label() );
743 TQString s1 = i18n("Refresh IMAP Cache");
744 TQString s2 = i18n("&Refresh");
745 if( KMessageBox::warningContinueCancel( 0, str, s1, s2 ) ==
746 KMessageBox::Continue )
747 account()->invalidateIMAPFolders( this );
748 } else {
749 // Rebuild index file
750 switch ( rc ) {
751 case DImapTroubleShootDialog::ReindexAll:
752 {
753 KMFolderCachedImap *rootStorage = dynamic_cast<KMFolderCachedImap*>( account()->rootFolder() );
754 if ( rootStorage )
755 rootStorage->createIndexFromContentsRecursive();
756 break;
757 }
758 case DImapTroubleShootDialog::ReindexCurrent:
759 createIndexFromContents();
760 break;
761 case DImapTroubleShootDialog::ReindexRecursive:
762 createIndexFromContentsRecursive();
763 break;
764 default:
765 return;
766 }
767 KMessageBox::information( 0, i18n( "The index of this folder has been "
768 "recreated." ) );
769 writeIndex();
770 kmkernel->getKMMainWidget()->folderSelected();
771 }
772}
773
774void KMFolderCachedImap::serverSync( bool recurse, bool quotaOnly )
775{
776 if( mSyncState != SYNC_STATE_INITIAL ) {
777 if( KMessageBox::warningYesNo( 0, i18n("Folder %1 is not in initial sync state (state was %2). Do you want to reset it to initial sync state and sync anyway?" ).arg( imapPath() ).arg( mSyncState ), TQString(), i18n("Reset && Sync"), KStdGuiItem::cancel() ) == KMessageBox::Yes ) {
778 mSyncState = SYNC_STATE_INITIAL;
779 } else return;
780 }
781
782 mRecurse = recurse;
783 mQuotaOnly = quotaOnly;
784 assert( account() );
785
786 ProgressItem *progressItem = mAccount->mailCheckProgressItem();
787 if ( progressItem ) {
788 progressItem->reset();
789 progressItem->setTotalItems( 100 );
790 }
791 mProgress = 0;
792
793#if 0
794 if( mHoldSyncs ) {
795 // All done for this folder.
796 account()->mailCheckProgressItem()->setProgress( 100 );
797 mProgress = 100; // all done
798 newState( mProgress, i18n("Synchronization skipped"));
799 mSyncState = SYNC_STATE_INITIAL;
800 emit folderComplete( this, true );
801 return;
802 }
803#endif
804 mTentativeHighestUid = 0; // reset, last sync could have been canceled
805
806 serverSyncInternal();
807}
808
809TQString KMFolderCachedImap::state2String( int state ) const
810{
811 switch( state ) {
812 case SYNC_STATE_INITIAL: return "SYNC_STATE_INITIAL";
813 case SYNC_STATE_GET_USERRIGHTS: return "SYNC_STATE_GET_USERRIGHTS";
814 case SYNC_STATE_PUT_MESSAGES: return "SYNC_STATE_PUT_MESSAGES";
815 case SYNC_STATE_UPLOAD_FLAGS: return "SYNC_STATE_UPLOAD_FLAGS";
816 case SYNC_STATE_CREATE_SUBFOLDERS: return "SYNC_STATE_CREATE_SUBFOLDERS";
817 case SYNC_STATE_LIST_SUBFOLDERS: return "SYNC_STATE_LIST_SUBFOLDERS";
818 case SYNC_STATE_LIST_NAMESPACES: return "SYNC_STATE_LIST_NAMESPACES";
819 case SYNC_STATE_LIST_SUBFOLDERS2: return "SYNC_STATE_LIST_SUBFOLDERS2";
820 case SYNC_STATE_DELETE_SUBFOLDERS: return "SYNC_STATE_DELETE_SUBFOLDERS";
821 case SYNC_STATE_LIST_MESSAGES: return "SYNC_STATE_LIST_MESSAGES";
822 case SYNC_STATE_DELETE_MESSAGES: return "SYNC_STATE_DELETE_MESSAGES";
823 case SYNC_STATE_GET_MESSAGES: return "SYNC_STATE_GET_MESSAGES";
824 case SYNC_STATE_EXPUNGE_MESSAGES: return "SYNC_STATE_EXPUNGE_MESSAGES";
825 case SYNC_STATE_HANDLE_INBOX: return "SYNC_STATE_HANDLE_INBOX";
826 case SYNC_STATE_TEST_ANNOTATIONS: return "SYNC_STATE_TEST_ANNOTATIONS";
827 case SYNC_STATE_GET_ANNOTATIONS: return "SYNC_STATE_GET_ANNOTATIONS";
828 case SYNC_STATE_SET_ANNOTATIONS: return "SYNC_STATE_SET_ANNOTATIONS";
829 case SYNC_STATE_GET_ACLS: return "SYNC_STATE_GET_ACLS";
830 case SYNC_STATE_SET_ACLS: return "SYNC_STATE_SET_ACLS";
831 case SYNC_STATE_GET_QUOTA: return "SYNC_STATE_GET_QUOTA";
832 case SYNC_STATE_FIND_SUBFOLDERS: return "SYNC_STATE_FIND_SUBFOLDERS";
833 case SYNC_STATE_SYNC_SUBFOLDERS: return "SYNC_STATE_SYNC_SUBFOLDERS";
834 case SYNC_STATE_RENAME_FOLDER: return "SYNC_STATE_RENAME_FOLDER";
835 case SYNC_STATE_CHECK_UIDVALIDITY: return "SYNC_STATE_CHECK_UIDVALIDITY";
836 case SYNC_STATE_CLOSE: return "SYNC_STATE_CLOSE";
837 case SYNC_STATE_GET_SUBFOLDER_QUOTA: return "SYNC_STATE_GET_SUBFOLDER_QUOTA";
838 default: return "Unknown state";
839 }
840}
841
842/*
843 Progress calculation: each step is assigned a span. Initially the total is 100.
844 But if we skip a step, don't increase the progress.
845 This leaves more room for the step a with variable size (get_messages)
846 connecting 5
847 getuserrights 5
848 rename 5
849 check_uidvalidity 5
850 create_subfolders 5
851 put_messages 10 (but it can take a very long time, with many messages....)
852 upload_flags 5
853 list_subfolders 5
854 list_subfolders2 0 (all local)
855 delete_subfolders 5
856 list_messages 10
857 delete_messages 10
858 expunge_messages 5
859 get_messages variable (remaining-5) i.e. minimum 15.
860 check_annotations 0 (rare)
861 set_annotations 0 (rare)
862 get_annotations 2
863 set_acls 0 (rare)
864 get_acls 3
865
866 noContent folders have only a few of the above steps
867 (permissions, and all subfolder stuff), so its steps should be given more span
868
869 */
870
871// While the server synchronization is running, mSyncState will hold
872// the state that should be executed next
873void KMFolderCachedImap::serverSyncInternal()
874{
875 // This is used to stop processing when we're about to exit
876 // and the current job wasn't cancellable.
877 // For user-requested abort, we'll use signalAbortRequested instead.
878 if( kmkernel->mailCheckAborted() ) {
879 resetSyncState();
880 emit folderComplete( this, false );
881 return;
882 }
883
884 //kdDebug(5006) << label() << ": " << state2String( mSyncState ) << endl;
885 switch( mSyncState ) {
886 case SYNC_STATE_INITIAL:
887 {
888 mProgress = 0;
889 foldersForDeletionOnServer.clear();
890 newState( mProgress, i18n("Synchronizing"));
891
892 open("cachedimap");
893 if ( !noContent() )
894 mAccount->addLastUnreadMsgCount( this, countUnread() );
895
896 // Connect to the server (i.e. prepare the slave)
897 ImapAccountBase::ConnectionState cs = mAccount->makeConnection();
898 if ( cs == ImapAccountBase::Error ) {
899 // Cancelled by user, or slave can't start
900 // kdDebug(5006) << "makeConnection said Error, aborting." << endl;
901 // We stop here. We're already in SYNC_STATE_INITIAL for the next time.
902 newState( mProgress, i18n( "Error connecting to server %1" ).arg( mAccount->host() ) );
903 close("cachedimap");
904 emit folderComplete(this, false);
905 break;
906 } else if ( cs == ImapAccountBase::Connecting ) {
907 mAccount->setAnnotationCheckPassed( false );
908 // kdDebug(5006) << "makeConnection said Connecting, waiting for signal." << endl;
909 newState( mProgress, i18n("Connecting to %1").arg( mAccount->host() ) );
910 // We'll wait for the connectionResult signal from the account.
911 connect( mAccount, TQ_SIGNAL( connectionResult(int, const TQString&) ),
912 this, TQ_SLOT( slotConnectionResult(int, const TQString&) ) );
913 break;
914 } else {
915 // Connected
916 // kdDebug(5006) << "makeConnection said Connected, proceeding." << endl;
917 mSyncState = SYNC_STATE_GET_USERRIGHTS;
918 // Fall through to next state
919 }
920 }
921
922
923 case SYNC_STATE_GET_USERRIGHTS:
924
925 // Now we have started the sync, emit changed() so that the folder tree can update the status
926 emit syncStateChanged();
927 //kdDebug(5006) << "===== Syncing " << ( mImapPath.isEmpty() ? label() : mImapPath ) << endl;
928
929 mSyncState = SYNC_STATE_RENAME_FOLDER;
930
931 if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
932 // Check the user's own rights. We do this every time in case they changed.
933 mOldUserRights = mUserRights;
934 newState( mProgress, i18n("Checking permissions"));
935 connect( mAccount, TQ_SIGNAL( receivedUserRights( KMFolder* ) ),
936 this, TQ_SLOT( slotReceivedUserRights( KMFolder* ) ) );
937 mAccount->getUserRights( folder(), imapPath() ); // after connecting, due to the INBOX case
938 break;
939 }
940
941 else if ( !mQuotaOnly && noContent() && mAccount->hasACLSupport() ) {
942 // This is a no content folder. The server would simply say that mailbox does not exist when
943 // querying the rights for it. So pretend we have no rights.
944 mUserRights = 0;
945 mUserRightsState = KMail::ACLJobs::Ok;
946 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
947 }
948
949 case SYNC_STATE_RENAME_FOLDER:
950 {
951 mSyncState = SYNC_STATE_CHECK_UIDVALIDITY;
952 // Returns the new name if the folder was renamed, empty otherwise.
953 bool isResourceFolder = kmkernel->iCalIface().isStandardResourceFolder( folder() );
954 TQString newName = mAccount->renamedFolder( imapPath() );
955 if ( !newName.isEmpty() && !folder()->isSystemFolder() && !isResourceFolder ) {
956 newState( mProgress, i18n("Renaming folder") );
957 CachedImapJob *job = new CachedImapJob( newName, CachedImapJob::tRenameFolder, this );
958 connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
959 connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( slotRenameFolderFinished() ) );
960 job->start();
961 break;
962 }
963 }
964
965 case SYNC_STATE_CHECK_UIDVALIDITY:
966 mSyncState = SYNC_STATE_CREATE_SUBFOLDERS;
967 if( !mQuotaOnly && !noContent() ) {
968 checkUidValidity();
969 break;
970 }
971 // Else carry on
972
973 case SYNC_STATE_CREATE_SUBFOLDERS:
974 mSyncState = SYNC_STATE_PUT_MESSAGES;
975 if ( !mQuotaOnly ) {
976 createNewFolders();
977 break;
978 }
979
980 case SYNC_STATE_PUT_MESSAGES:
981 mSyncState = SYNC_STATE_UPLOAD_FLAGS;
982 if( !mQuotaOnly && !noContent() ) {
983 uploadNewMessages();
984 break;
985 }
986 // Else carry on
987 case SYNC_STATE_UPLOAD_FLAGS:
988 mSyncState = SYNC_STATE_LIST_NAMESPACES;
989 if( !mQuotaOnly && !noContent() ) {
990 // We haven't downloaded messages yet, so we need to build the map.
991 if( uidMapDirty )
992 reloadUidMap();
993 // Upload flags, unless we know from the ACL that we're not allowed
994 // to do that or they did not change locally
995 if ( mUserRightsState != KMail::ACLJobs::Ok ||
996 ( mUserRights & (KMail::ACLJobs::WriteFlags ) ) ) {
997 if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
998 uploadFlags();
999 break;
1000 } else {
1001 //kdDebug(5006) << "Skipping flags upload, folder unchanged: " << label() << endl;
1002 }
1003 } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
1004 if ( !mUIDsOfLocallyChangedStatuses.empty() || mStatusChangedLocally ) {
1005 uploadSeenFlags();
1006 break;
1007 }
1008 }
1009 }
1010 // Else carry on
1011
1012 case SYNC_STATE_LIST_NAMESPACES:
1013 if ( !mQuotaOnly && this == mAccount->rootFolder() ) {
1014 listNamespaces();
1015 break;
1016 }
1017 mSyncState = SYNC_STATE_LIST_SUBFOLDERS;
1018 // Else carry on
1019
1020 case SYNC_STATE_LIST_SUBFOLDERS:
1021 newState( mProgress, i18n("Retrieving folderlist"));
1022 mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
1023 if ( !mQuotaOnly ) {
1024 if( !listDirectory() ) {
1025 mSyncState = SYNC_STATE_INITIAL;
1026 KMessageBox::error(0, i18n("Error while retrieving the folderlist"));
1027 }
1028 break;
1029 }
1030
1031 case SYNC_STATE_LIST_SUBFOLDERS2:
1032 mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
1033 mProgress += 10;
1034 if ( !mQuotaOnly ) {
1035 newState( mProgress, i18n("Retrieving subfolders"));
1036 listDirectory2();
1037 break;
1038 }
1039
1040 case SYNC_STATE_DELETE_SUBFOLDERS:
1041 mSyncState = SYNC_STATE_LIST_MESSAGES;
1042 if( !mQuotaOnly && !foldersForDeletionOnServer.isEmpty() ) {
1043 newState( mProgress, i18n("Deleting folders from server"));
1044 CachedImapJob* job = new CachedImapJob( foldersForDeletionOnServer,
1045 CachedImapJob::tDeleteFolders, this );
1046 connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
1047 connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( slotFolderDeletionOnServerFinished() ) );
1048 job->start();
1049 break;
1050 }
1051 // Not needed, the next step emits newState very quick
1052 //newState( mProgress, i18n("No folders to delete from server"));
1053 // Carry on
1054
1055 case SYNC_STATE_LIST_MESSAGES:
1056 mSyncState = SYNC_STATE_DELETE_MESSAGES;
1057 if( !mQuotaOnly && !noContent() ) {
1058 newState( mProgress, i18n("Retrieving message list"));
1059 listMessages();
1060 break;
1061 }
1062 // Else carry on
1063
1064 case SYNC_STATE_DELETE_MESSAGES:
1065 mSyncState = SYNC_STATE_EXPUNGE_MESSAGES;
1066 if( !mQuotaOnly && !noContent() ) {
1067 if( deleteMessages() ) {
1068 // Fine, we will continue with the next state
1069 } else {
1070 // No messages to delete, skip to GET_MESSAGES
1071 newState( mProgress, i18n("No messages to delete..."));
1072 mSyncState = SYNC_STATE_GET_MESSAGES;
1073 serverSyncInternal();
1074 }
1075 break;
1076 }
1077 // Else carry on
1078
1079 case SYNC_STATE_EXPUNGE_MESSAGES:
1080 mSyncState = SYNC_STATE_GET_MESSAGES;
1081 if( !mQuotaOnly && !noContent() ) {
1082 newState( mProgress, i18n("Expunging deleted messages"));
1083 CachedImapJob *job = new CachedImapJob( TQString(),
1084 CachedImapJob::tExpungeFolder, this );
1085 connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
1086 connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1087 job->start();
1088 break;
1089 }
1090 // Else carry on
1091
1092 case SYNC_STATE_GET_MESSAGES:
1093 mSyncState = SYNC_STATE_HANDLE_INBOX;
1094 if( !mQuotaOnly && !noContent() ) {
1095 if( !mMsgsForDownload.isEmpty() ) {
1096 newState( mProgress, i18n("Retrieving one new message","Retrieving %n new messages",mMsgsForDownload.size()));
1097 CachedImapJob *job = new CachedImapJob( mMsgsForDownload,
1098 CachedImapJob::tGetMessage,
1099 this );
1100 connect( job, TQ_SIGNAL( progress(unsigned long, unsigned long) ),
1101 this, TQ_SLOT( slotProgress(unsigned long, unsigned long) ) );
1102 connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( slotUpdateLastUid() ) );
1103 connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1104 job->start();
1105 mMsgsForDownload.clear();
1106 break;
1107 } else {
1108 newState( mProgress, i18n("No new messages from server"));
1109 /* There were no messages to download, but it could be that we uploaded some
1110 which we didn't need to download again because we already knew the uid.
1111 Now that we are sure there is nothing to download, and everything that had
1112 to be deleted on the server has been deleted, adjust our local notion of the
1113 highes uid seen thus far. */
1114 slotUpdateLastUid();
1115 if( mLastUid == 0 && uidWriteTimer == -1 ) {
1116 // This is probably a new and empty folder. Write the UID cache
1117 if ( writeUidCache() == -1 ) {
1118 resetSyncState();
1119 emit folderComplete( this, false );
1120 return;
1121 }
1122 }
1123 }
1124 }
1125
1126 // Else carry on
1127
1128 case SYNC_STATE_HANDLE_INBOX:
1129 // Wrap up the 'download emails' stage. We always end up at 95 here.
1130 mProgress = 95;
1131 mSyncState = SYNC_STATE_TEST_ANNOTATIONS;
1132
1133 #define KOLAB_FOLDERTEST "/vendor/kolab/folder-test"
1134 case SYNC_STATE_TEST_ANNOTATIONS:
1135 mSyncState = SYNC_STATE_GET_ANNOTATIONS;
1136 // The first folder with user rights to write annotations
1137 if( !mQuotaOnly && !mAccount->annotationCheckPassed() &&
1138 ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) )
1139 && !imapPath().isEmpty() && imapPath() != "/" ) {
1140 kdDebug(5006) << "Setting test attribute on folder: "<< folder()->prettyURL() << endl;
1141 newState( mProgress, i18n("Checking annotation support"));
1142
1143 KURL url = mAccount->getUrl();
1144 url.setPath( imapPath() );
1145 KMail::AnnotationList annotations; // to be set
1146
1147 KMail::AnnotationAttribute attr( KOLAB_FOLDERTEST, "value.shared", "true" );
1148 annotations.append( attr );
1149
1150 kdDebug(5006) << "Setting test attribute to "<< url << endl;
1151 TDEIO::Job* job = AnnotationJobs::multiSetAnnotation( mAccount->slave(),
1152 url, annotations );
1153 ImapAccountBase::jobData jd( url.url(), folder() );
1154 jd.cancellable = true; // we can always do so later
1155 mAccount->insertJob(job, jd);
1156 connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1157 TQ_SLOT(slotTestAnnotationResult(TDEIO::Job *)));
1158 break;
1159 }
1160
1161 case SYNC_STATE_GET_ANNOTATIONS: {
1162#define KOLAB_FOLDERTYPE "/vendor/kolab/folder-type"
1163#define KOLAB_INCIDENCESFOR "/vendor/kolab/incidences-for"
1164#define KOLAB_SHAREDSEEN "/vendor/cmu/cyrus-imapd/sharedseen"
1165//#define KOLAB_FOLDERTYPE "/comment" //for testing, while cyrus-imap doesn't support /vendor/*
1166 mSyncState = SYNC_STATE_SET_ANNOTATIONS;
1167
1168 bool needToGetInitialAnnotations = false;
1169 if ( !mQuotaOnly && !noContent() ) {
1170 // for a folder we didn't create ourselves: get annotation from server
1171 if ( mAnnotationFolderType == "FROMSERVER" ) {
1172 needToGetInitialAnnotations = true;
1173 mAnnotationFolderType = TQString();
1174 } else {
1175 updateAnnotationFolderType();
1176 }
1177 }
1178
1179 // First retrieve the annotation, so that we know we have to set it if it's not set.
1180 // On the other hand, if the user changed the contentstype, there's no need to get first.
1181 if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
1182 ( kmkernel->iCalIface().isEnabled() || needToGetInitialAnnotations ) ) {
1183 TQStringList annotations; // list of annotations to be fetched
1184 if ( !mAnnotationFolderTypeChanged || mAnnotationFolderType.isEmpty() )
1185 annotations << KOLAB_FOLDERTYPE;
1186 if ( !mIncidencesForChanged )
1187 annotations << KOLAB_INCIDENCESFOR;
1188 if ( !mSharedSeenFlagsChanged )
1189 annotations << KOLAB_SHAREDSEEN;
1190 if ( !annotations.isEmpty() ) {
1191 newState( mProgress, i18n("Retrieving annotations"));
1192 KURL url = mAccount->getUrl();
1193 url.setPath( imapPath() );
1195 AnnotationJobs::multiGetAnnotation( mAccount->slave(), url, annotations );
1196 ImapAccountBase::jobData jd( url.url(), folder() );
1197 jd.cancellable = true;
1198 mAccount->insertJob(job, jd);
1199
1200 connect( job, TQ_SIGNAL(annotationResult(const TQString&, const TQString&, bool)),
1201 TQ_SLOT(slotAnnotationResult(const TQString&, const TQString&, bool)) );
1202 connect( job, TQ_SIGNAL(result(TDEIO::Job *)),
1203 TQ_SLOT(slotGetAnnotationResult(TDEIO::Job *)) );
1204 break;
1205 }
1206 }
1207 } // case
1208 case SYNC_STATE_SET_ANNOTATIONS:
1209
1210 mSyncState = SYNC_STATE_SET_ACLS;
1211 if ( !mQuotaOnly && !noContent() && mAccount->hasAnnotationSupport() &&
1212 ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) {
1213 newState( mProgress, i18n("Setting annotations"));
1214 KURL url = mAccount->getUrl();
1215 url.setPath( imapPath() );
1216 KMail::AnnotationList annotations; // to be set
1217 if ( mAnnotationFolderTypeChanged && !mAnnotationFolderType.isEmpty() ) {
1218 KMail::AnnotationAttribute attr( KOLAB_FOLDERTYPE, "value.shared", mAnnotationFolderType );
1219 annotations.append( attr );
1220 kdDebug(5006) << "Setting folder-type annotation for " << label() << " to " << mAnnotationFolderType << endl;
1221 }
1222 if ( mIncidencesForChanged ) {
1223 const TQString val = incidencesForToString( mIncidencesFor );
1224 KMail::AnnotationAttribute attr( KOLAB_INCIDENCESFOR, "value.shared", val );
1225 annotations.append( attr );
1226 kdDebug(5006) << "Setting incidences-for annotation for " << label() << " to " << val << endl;
1227 }
1228 if ( mSharedSeenFlagsChanged ) {
1229 const TQString val = mSharedSeenFlags ? "true" : "false";
1230 KMail::AnnotationAttribute attr( KOLAB_SHAREDSEEN, "value.shared", val );
1231 annotations.append( attr );
1232 kdDebug(5006) << k_funcinfo << "Setting sharedseen annotation for " << label() << " to " << val << endl;
1233 }
1234 if ( !annotations.isEmpty() ) {
1235 TDEIO::Job* job =
1236 AnnotationJobs::multiSetAnnotation( mAccount->slave(), url, annotations );
1237 ImapAccountBase::jobData jd( url.url(), folder() );
1238 jd.cancellable = true; // we can always do so later
1239 mAccount->insertJob(job, jd);
1240
1241 connect(job, TQ_SIGNAL(annotationChanged( const TQString&, const TQString&, const TQString& ) ),
1242 TQ_SLOT( slotAnnotationChanged( const TQString&, const TQString&, const TQString& ) ));
1243 connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1244 TQ_SLOT(slotSetAnnotationResult(TDEIO::Job *)));
1245 break;
1246 }
1247 }
1248
1249 case SYNC_STATE_SET_ACLS:
1250 mSyncState = SYNC_STATE_GET_ACLS;
1251
1252 if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() &&
1253 ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ACLJobs::Administer ) ) ) {
1254 bool hasChangedACLs = false;
1255 ACLList::ConstIterator it = mACLList.begin();
1256 for ( ; it != mACLList.end() && !hasChangedACLs; ++it ) {
1257 hasChangedACLs = (*it).changed;
1258 }
1259 if ( hasChangedACLs ) {
1260 newState( mProgress, i18n("Setting permissions"));
1261 KURL url = mAccount->getUrl();
1262 url.setPath( imapPath() );
1263 TDEIO::Job* job = KMail::ACLJobs::multiSetACL( mAccount->slave(), url, mACLList );
1264 ImapAccountBase::jobData jd( url.url(), folder() );
1265 mAccount->insertJob(job, jd);
1266
1267 connect(job, TQ_SIGNAL(result(TDEIO::Job *)),
1268 TQ_SLOT(slotMultiSetACLResult(TDEIO::Job *)));
1269 connect(job, TQ_SIGNAL(aclChanged( const TQString&, int )),
1270 TQ_SLOT(slotACLChanged( const TQString&, int )) );
1271 break;
1272 }
1273 }
1274
1275 case SYNC_STATE_GET_ACLS:
1276 mSyncState = SYNC_STATE_FIND_SUBFOLDERS;
1277
1278 if( !mQuotaOnly && !noContent() && mAccount->hasACLSupport() ) {
1279 newState( mProgress, i18n( "Retrieving permissions" ) );
1280 mAccount->getACL( folder(), mImapPath );
1281 connect( mAccount, TQ_SIGNAL(receivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )),
1282 this, TQ_SLOT(slotReceivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )) );
1283 break;
1284 }
1285 case SYNC_STATE_FIND_SUBFOLDERS:
1286 {
1287 mSyncState = SYNC_STATE_SYNC_SUBFOLDERS;
1288 mSomeSubFolderCloseToQuotaChanged = false;
1289 buildSubFolderList();
1290 }
1291
1292 // Carry on
1293 case SYNC_STATE_SYNC_SUBFOLDERS:
1294 syncNextSubFolder( false );
1295 break;
1296 case SYNC_STATE_GET_SUBFOLDER_QUOTA:
1297
1298 // Sync the subfolders again, so that the quota information is updated for all. This state is
1299 // only entered if the close to quota property of one subfolder changed in the previous state.
1300 syncNextSubFolder( true );
1301 break;
1302 case SYNC_STATE_GET_QUOTA:
1303 mSyncState = SYNC_STATE_CLOSE;
1304 if( !noContent() && mAccount->hasQuotaSupport() ) {
1305 mProgress = 98;
1306 newState( mProgress, i18n("Getting quota information"));
1307 KURL url = mAccount->getUrl();
1308 url.setPath( imapPath() );
1309 TDEIO::Job* job = KMail::QuotaJobs::getStorageQuota( mAccount->slave(), url );
1310 ImapAccountBase::jobData jd( url.url(), folder() );
1311 mAccount->insertJob(job, jd);
1312 connect( job, TQ_SIGNAL( storageQuotaResult( const QuotaInfo& ) ),
1313 TQ_SLOT( slotStorageQuotaResult( const QuotaInfo& ) ) );
1314 connect( job, TQ_SIGNAL(result(TDEIO::Job *)),
1315 TQ_SLOT(slotQuotaResult(TDEIO::Job *)) );
1316 break;
1317 }
1318 case SYNC_STATE_CLOSE:
1319 {
1320 mProgress = 100; // all done
1321 newState( mProgress, i18n("Synchronization done"));
1322 KURL url = mAccount->getUrl();
1323 url.setPath( imapPath() );
1324 kmkernel->iCalIface().folderSynced( folder(), url );
1325
1326 mSyncState = SYNC_STATE_INITIAL;
1327 mAccount->addUnreadMsgCount( this, countUnread() ); // before closing
1328 close( "cachedimap" );
1329 emit syncStateChanged();
1330 emit folderComplete( this, true );
1331 }
1332 break;
1333
1334 default:
1335 kdDebug(5006) << "KMFolderCachedImap::serverSyncInternal() WARNING: no such state "
1336 << mSyncState << endl;
1337 }
1338}
1339
1340void KMFolderCachedImap::syncNextSubFolder( bool secondSync )
1341{
1342 if( mCurrentSubfolder ) {
1343 disconnectSubFolderSignals();
1344 }
1345
1346 if( mSubfoldersForSync.isEmpty() ) {
1347
1348 // Sync finished, and a close to quota property of an subfolder changed, therefore go into
1349 // the SYNC_STATE_GET_SUBFOLDER_QUOTA state and sync again
1350 if ( mSomeSubFolderCloseToQuotaChanged && mRecurse && !secondSync ) {
1351 buildSubFolderList();
1352 mSyncState = SYNC_STATE_GET_SUBFOLDER_QUOTA;
1353 serverSyncInternal();
1354 }
1355
1356 else {
1357
1358 // Quota checking has to come after syncing subfolder, otherwise the quota information would
1359 // be outdated, since the subfolders can change in size during the syncing.
1360 // https://issues.kolab.org/issue4066
1361 mSyncState = SYNC_STATE_GET_QUOTA;
1362 serverSyncInternal();
1363 }
1364 } else {
1365 mCurrentSubfolder = mSubfoldersForSync.front();
1366 mSubfoldersForSync.pop_front();
1367 if ( mCurrentSubfolder ) {
1368 connect( mCurrentSubfolder, TQ_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
1369 this, TQ_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
1370 connect( mCurrentSubfolder, TQ_SIGNAL( closeToQuotaChanged() ),
1371 this, TQ_SLOT( slotSubFolderCloseToQuotaChanged() ) );
1372
1373 //kdDebug(5006) << "Sync'ing subfolder " << mCurrentSubfolder->imapPath() << endl;
1374 assert( !mCurrentSubfolder->imapPath().isEmpty() );
1375 mCurrentSubfolder->setAccount( account() );
1376 const bool recurse = mCurrentSubfolder->noChildren() ? false : true;
1377 const bool quotaOnly = secondSync || mQuotaOnly;
1378 mCurrentSubfolder->serverSync( recurse, quotaOnly );
1379 }
1380 else {
1381 // mCurrentSubfolder is empty, probably because it was deleted while syncing. Go on with the
1382 // next subfolder instead.
1383 syncNextSubFolder( secondSync );
1384 }
1385 }
1386}
1387
1388void KMFolderCachedImap::buildSubFolderList()
1389{
1390 mSubfoldersForSync.clear();
1391 mCurrentSubfolder = 0;
1392 if( folder() && folder()->child() ) {
1393 KMFolderNode *node = folder()->child()->first();
1394 while( node ) {
1395 if( !node->isDir() ) {
1396 KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
1397 const bool folderIsNew = mNewlyCreatedSubfolders.contains( TQGuardedPtr<KMFolderCachedImap>( storage ) );
1398 // Only sync folders that have been accepted by the server
1399 if ( !storage->imapPath().isEmpty()
1400 // and that were not just deleted from it
1401 && !foldersForDeletionOnServer.contains( storage->imapPath() ) ) {
1402 if ( mRecurse || folderIsNew ) {
1403 mSubfoldersForSync << storage;
1404 }
1405 } else {
1406 kdDebug(5006) << "Do not add " << storage->label()
1407 << " to synclist" << endl;
1408 }
1409 }
1410 node = folder()->child()->next();
1411 }
1412 }
1413
1414 mNewlyCreatedSubfolders.clear();
1415}
1416
1417void KMFolderCachedImap::disconnectSubFolderSignals()
1418{
1419 disconnect( mCurrentSubfolder, TQ_SIGNAL( folderComplete(KMFolderCachedImap*, bool) ),
1420 this, TQ_SLOT( slotSubFolderComplete(KMFolderCachedImap*, bool) ) );
1421 disconnect( mCurrentSubfolder, TQ_SIGNAL( closeToQuotaChanged() ),
1422 this, TQ_SLOT( slotSubFolderCloseToQuotaChanged() ) );
1423 mCurrentSubfolder = 0;
1424}
1425
1426/* Connected to the imap account's connectionResult signal.
1427 Emitted when the slave connected or failed to connect.
1428*/
1429void KMFolderCachedImap::slotConnectionResult( int errorCode, const TQString& errorMsg )
1430{
1431 disconnect( mAccount, TQ_SIGNAL( connectionResult(int, const TQString&) ),
1432 this, TQ_SLOT( slotConnectionResult(int, const TQString&) ) );
1433 if ( !errorCode ) {
1434 // Success
1435 mSyncState = SYNC_STATE_GET_USERRIGHTS;
1436 mProgress += 5;
1437 serverSyncInternal();
1438 } else {
1439 // Error (error message already shown by the account)
1440 newState( mProgress, TDEIO::buildErrorString( errorCode, errorMsg ));
1441 emit folderComplete(this, false);
1442 }
1443}
1444
1445/* find new messages (messages without a UID) */
1446TQValueList<unsigned long> KMFolderCachedImap::findNewMessages()
1447{
1448 TQValueList<unsigned long> result;
1449 for( int i = 0; i < count(); ++i ) {
1450 KMMsgBase *msg = getMsgBase( i );
1451 if( !msg ) continue; /* what goes on if getMsg() returns 0? */
1452 if ( msg->UID() == 0 )
1453 result.append( msg->getMsgSerNum() );
1454 }
1455 return result;
1456}
1457
1458/* Upload new messages to server */
1459void KMFolderCachedImap::uploadNewMessages()
1460{
1461 TQValueList<unsigned long> newMsgs = findNewMessages();
1462 if( !newMsgs.isEmpty() ) {
1463 if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & ( KMail::ACLJobs::Insert ) ) ) {
1464 newState( mProgress, i18n("Uploading messages to server"));
1465 CachedImapJob *job = new CachedImapJob( newMsgs, CachedImapJob::tPutMessage, this );
1466 connect( job, TQ_SIGNAL( progress( unsigned long, unsigned long) ),
1467 this, TQ_SLOT( slotPutProgress(unsigned long, unsigned long) ) );
1468 connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1469 job->start();
1470 return;
1471 } else {
1472 KMCommand *command = rescueUnsyncedMessages();
1473 connect( command, TQ_SIGNAL( completed( KMCommand * ) ),
1474 this, TQ_SLOT( serverSyncInternal() ) );
1475 }
1476 } else { // nothing to upload
1477 if ( mUserRights != mOldUserRights && (mOldUserRights & KMail::ACLJobs::Insert)
1478 && !(mUserRights & KMail::ACLJobs::Insert) ) {
1479 // write access revoked
1480 KMessageBox::information( 0, i18n("<p>Your access rights to folder <b>%1</b> have been restricted, "
1481 "it will no longer be possible to add messages to this folder.</p>").arg( folder()->prettyURL() ),
1482 i18n("Acces rights revoked"), "KMailACLRevocationNotification" );
1483 }
1484 }
1485 newState( mProgress, i18n("No messages to upload to server"));
1486 serverSyncInternal();
1487}
1488
1489/* Progress info during uploadNewMessages */
1490void KMFolderCachedImap::slotPutProgress( unsigned long done, unsigned long total )
1491{
1492 // (going from mProgress to mProgress+10)
1493 int progressSpan = 10;
1494 newState( mProgress + (progressSpan * done) / total, TQString() );
1495 if ( done == total ) // we're done
1496 mProgress += progressSpan;
1497}
1498
1499/* Upload message flags to server */
1500void KMFolderCachedImap::uploadFlags()
1501{
1502 if ( !uidMap.isEmpty() ) {
1503 mStatusFlagsJobs = 0;
1504 newState( mProgress, i18n("Uploading status of messages to server"));
1505
1506 // FIXME DUPLICATED FROM KMFOLDERIMAP
1507 TQMap< TQString, TQStringList > groups;
1508 //open(); //already done
1509 for( int i = 0; i < count(); ++i ) {
1510 KMMsgBase* msg = getMsgBase( i );
1511 if( !msg || msg->UID() == 0 )
1512 // Either not a valid message or not one that is on the server yet
1513 continue;
1514 if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
1515 && !mStatusChangedLocally ) {
1516 // This message has not had its status changed locally
1517 continue;
1518 }
1519
1520 TQString flags = KMFolderImap::statusToFlags(msg->status(), mPermanentFlags);
1521 // Collect uids for each typem of flags.
1522 TQString uid;
1523 uid.setNum( msg->UID() );
1524 groups[flags].append(uid);
1525 }
1526 TQMapIterator< TQString, TQStringList > dit;
1527 for( dit = groups.begin(); dit != groups.end(); ++dit ) {
1528 TQCString flags = dit.key().latin1();
1529 TQStringList sets = KMFolderImap::makeSets( (*dit), true );
1530 mStatusFlagsJobs += sets.count(); // ### that's not in kmfolderimap....
1531 // Send off a status setting job for each set.
1532 for( TQStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
1533 TQString imappath = imapPath() + ";UID=" + ( *slit );
1534 mAccount->setImapStatus(folder(), imappath, flags);
1535 }
1536 }
1537 // FIXME END DUPLICATED FROM KMFOLDERIMAP
1538
1539 if ( mStatusFlagsJobs ) {
1540 connect( mAccount, TQ_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ),
1541 this, TQ_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) );
1542 return;
1543 }
1544 }
1545 newState( mProgress, i18n("No messages to upload to server"));
1546 serverSyncInternal();
1547}
1548
1549void KMFolderCachedImap::uploadSeenFlags()
1550{
1551 if ( !uidMap.isEmpty() ) {
1552 mStatusFlagsJobs = 0;
1553 newState( mProgress, i18n("Uploading status of messages to server"));
1554
1555 TQValueList<ulong> seenUids, unseenUids;
1556 for( int i = 0; i < count(); ++i ) {
1557 KMMsgBase* msg = getMsgBase( i );
1558 if( !msg || msg->UID() == 0 )
1559 // Either not a valid message or not one that is on the server yet
1560 continue;
1561
1562 if ( mUIDsOfLocallyChangedStatuses.find( msg->UID() ) == mUIDsOfLocallyChangedStatuses.end()
1563 && !mStatusChangedLocally ) {
1564 // This message has not had its status changed locally
1565 continue;
1566 }
1567
1568 if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
1569 seenUids.append( msg->UID() );
1570 else
1571 unseenUids.append( msg->UID() );
1572 }
1573 if ( !seenUids.isEmpty() ) {
1574 TQStringList sets = KMFolderImap::makeSets( seenUids, true );
1575 mStatusFlagsJobs += sets.count();
1576 for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
1577 TQString imappath = imapPath() + ";UID=" + ( *it );
1578 mAccount->setImapSeenStatus( folder(), imappath, true );
1579 }
1580 }
1581 if ( !unseenUids.isEmpty() ) {
1582 TQStringList sets = KMFolderImap::makeSets( unseenUids, true );
1583 mStatusFlagsJobs += sets.count();
1584 for( TQStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
1585 TQString imappath = imapPath() + ";UID=" + ( *it );
1586 mAccount->setImapSeenStatus( folder(), imappath, false );
1587 }
1588 }
1589
1590 if ( mStatusFlagsJobs ) {
1591 connect( mAccount, TQ_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ),
1592 this, TQ_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) );
1593 return;
1594 }
1595 }
1596 newState( mProgress, i18n("No messages to upload to server"));
1597 serverSyncInternal();
1598}
1599
1600void KMFolderCachedImap::slotImapStatusChanged(KMFolder* folder, const TQString&, bool cont)
1601{
1602 if ( mSyncState == SYNC_STATE_INITIAL ){
1603 //kdDebug(5006) << "IMAP status changed but reset " << endl;
1604 return; // we were reset
1605 }
1606 //kdDebug(5006) << "IMAP status changed for folder: " << folder->prettyURL() << endl;
1607 if ( folder->storage() == this ) {
1608 --mStatusFlagsJobs;
1609 if ( mStatusFlagsJobs == 0 || !cont ) // done or aborting
1610 disconnect( mAccount, TQ_SIGNAL( imapStatusChanged(KMFolder*, const TQString&, bool) ),
1611 this, TQ_SLOT( slotImapStatusChanged(KMFolder*, const TQString&, bool) ) );
1612 if ( mStatusFlagsJobs == 0 && cont ) {
1613 mProgress += 5;
1614 serverSyncInternal();
1615 //kdDebug(5006) << "Proceeding with mailcheck." << endl;
1616 }
1617 }
1618}
1619
1620// This is not perfect, what if the status didn't really change? Oh well ...
1621void KMFolderCachedImap::setStatus( int idx, KMMsgStatus status, bool toggle)
1622{
1623 KMFolderMaildir::setStatus( idx, status, toggle );
1624 const KMMsgBase *msg = getMsgBase( idx );
1625 Q_ASSERT( msg );
1626 if ( msg )
1627 mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
1628}
1629
1630void KMFolderCachedImap::setStatus(TQValueList<int>& ids, KMMsgStatus status, bool toggle)
1631{
1632 KMFolderMaildir::setStatus(ids, status, toggle);
1633 for (TQValueList<int>::iterator it = ids.begin(); it != ids.end(); it++ ) {
1634 const KMMsgBase *msg = getMsgBase( *it );
1635 Q_ASSERT( msg );
1636 if ( msg )
1637 mUIDsOfLocallyChangedStatuses.insert( msg->UID() );
1638 }
1639}
1640
1641/* Upload new folders to server */
1642void KMFolderCachedImap::createNewFolders()
1643{
1644 TQValueList<KMFolderCachedImap*> newFolders = findNewFolders();
1645 //kdDebug(5006) << label() << " createNewFolders:" << newFolders.count() << " new folders." << endl;
1646 if( !newFolders.isEmpty() ) {
1647 newState( mProgress, i18n("Creating subfolders on server"));
1648 CachedImapJob *job = new CachedImapJob( newFolders, CachedImapJob::tAddSubfolders, this );
1649 connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ), this, TQ_SLOT( slotIncreaseProgress() ) );
1650 connect( job, TQ_SIGNAL( finished() ), this, TQ_SLOT( serverSyncInternal() ) );
1651 job->start();
1652 } else {
1653 serverSyncInternal();
1654 }
1655}
1656
1657TQValueList<KMFolderCachedImap*> KMFolderCachedImap::findNewFolders()
1658{
1659 TQValueList<KMFolderCachedImap*> newFolders;
1660 if( folder() && folder()->child() ) {
1661 KMFolderNode *node = folder()->child()->first();
1662 while( node ) {
1663 if( !node->isDir() ) {
1664 if( static_cast<KMFolder*>(node)->folderType() != KMFolderTypeCachedImap ) {
1665 kdError(5006) << "KMFolderCachedImap::findNewFolders(): ARGH!!! "
1666 << node->name() << " is not an IMAP folder\n";
1667 node = folder()->child()->next();
1668 assert(0);
1669 }
1670 KMFolderCachedImap* folder = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
1671 if( folder->imapPath().isEmpty() ) {
1672 newFolders << folder;
1673 }
1674 }
1675 node = folder()->child()->next();
1676 }
1677 }
1678 return newFolders;
1679}
1680
1681bool KMFolderCachedImap::deleteMessages()
1682{
1683 /* Delete messages from cache that are gone from the server */
1684 TQPtrList<KMMsgBase> msgsForDeletion;
1685
1686 // It is not possible to just go over all indices and remove
1687 // them one by one because the index list can get resized under
1688 // us. So use msg pointers instead
1689
1690 TQStringList uids;
1691 TQMap<ulong,int>::const_iterator it = uidMap.constBegin();
1692 for( ; it != uidMap.end(); it++ ) {
1693 ulong uid ( it.key() );
1694 if( uid!=0 && !uidsOnServer.find( uid ) ) {
1695 uids << TQString::number( uid );
1696 msgsForDeletion.append( getMsgBase( *it ) );
1697 }
1698 }
1699
1700 if( !msgsForDeletion.isEmpty() ) {
1701 if ( contentsType() != ContentsTypeMail ) {
1702 kdDebug(5006) << k_funcinfo << label() << " Going to locally delete " << msgsForDeletion.count()
1703 << " messages, with the uids " << uids.join( "," ) << endl;
1704 }
1705#if MAIL_LOSS_DEBUGGING
1706 if ( KMessageBox::warningYesNo(
1707 0, i18n( "<qt><p>Mails on the server in folder <b>%1</b> were deleted. "
1708 "Do you want to delete them locally?<br>UIDs: %2</p></qt>" )
1709 .arg( folder()->prettyURL() ).arg( uids.join(",") ) ) == KMessageBox::Yes )
1710#endif
1711 removeMsg( msgsForDeletion );
1712 }
1713
1714 if ( mUserRightsState == KMail::ACLJobs::Ok && !( mUserRights & KMail::ACLJobs::Delete ) )
1715 return false;
1716
1717 /* Delete messages from the server that we dont have anymore */
1718 if( !uidsForDeletionOnServer.isEmpty() ) {
1719 newState( mProgress, i18n("Deleting removed messages from server"));
1720 TQStringList sets = KMFolderImap::makeSets( uidsForDeletionOnServer, true );
1721 uidsForDeletionOnServer.clear();
1722 kdDebug(5006) << "Deleting " << sets.count() << " sets of messages from server folder " << imapPath() << endl;
1723 CachedImapJob *job = new CachedImapJob( sets, CachedImapJob::tDeleteMessage, this );
1724 connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ),
1725 this, TQ_SLOT( slotDeleteMessagesResult(KMail::FolderJob *) ) );
1726 job->start();
1727 return true;
1728 } else {
1729
1730 // Nothing to delete on the server, make sure the map is clear again.
1731 // Normally this wouldn't be necessary, but there can be stale maps because of
1732 // https://issues.kolab.org/issue3833.
1733 mDeletedUIDsSinceLastSync.clear();
1734 return false;
1735 }
1736}
1737
1738void KMFolderCachedImap::slotDeleteMessagesResult( KMail::FolderJob* job )
1739{
1740 if ( job->error() ) {
1741 // Skip the EXPUNGE state if deleting didn't work, no need to show two error messages
1742 mSyncState = SYNC_STATE_GET_MESSAGES;
1743 } else {
1744 // deleting on the server went fine, clear the pending deletions cache
1745 mDeletedUIDsSinceLastSync.clear();
1746 }
1747 mProgress += 10;
1748 serverSyncInternal();
1749}
1750
1751void KMFolderCachedImap::checkUidValidity() {
1752 // IMAP root folders don't seem to have a UID validity setting.
1753 // Also, don't try the uid validity on new folders
1754 if( imapPath().isEmpty() || imapPath() == "/" )
1755 // Just proceed
1756 serverSyncInternal();
1757 else {
1758 newState( mProgress, i18n("Checking folder validity"));
1759 CachedImapJob *job = new CachedImapJob( FolderJob::tCheckUidValidity, this );
1760 connect( job, TQ_SIGNAL(permanentFlags(int)), TQ_SLOT(slotPermanentFlags(int)) );
1761 connect( job, TQ_SIGNAL( result( KMail::FolderJob* ) ),
1762 this, TQ_SLOT( slotCheckUidValidityResult( KMail::FolderJob* ) ) );
1763 job->start();
1764 }
1765}
1766
1767void KMFolderCachedImap::slotCheckUidValidityResult( KMail::FolderJob* job )
1768{
1769 if ( job->error() ) { // there was an error and the user chose "continue"
1770 // We can't continue doing anything in the same folder though, it would delete all mails.
1771 // But we can continue to subfolders if any. Well we can also try annotation/acl stuff...
1772 mSyncState = SYNC_STATE_HANDLE_INBOX;
1773 }
1774 mProgress += 5;
1775 serverSyncInternal();
1776}
1777
1778void KMFolderCachedImap::slotPermanentFlags(int flags)
1779{
1780 mPermanentFlags = flags;
1781}
1782
1783/* This will only list the messages in a folder.
1784 No directory listing done*/
1785void KMFolderCachedImap::listMessages() {
1786 bool groupwareOnly = GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
1787 && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
1788 && folder()->isSystemFolder()
1789 && mImapPath == "/INBOX/";
1790 // Don't list messages on the root folder, and skip the inbox, if this is
1791 // the inbox of a groupware-only dimap account
1792 if( imapPath() == "/" || groupwareOnly ) {
1793 serverSyncInternal();
1794 return;
1795 }
1796
1797 if( !mAccount->slave() ) { // sync aborted
1798 resetSyncState();
1799 emit folderComplete( this, false );
1800 return;
1801 }
1802 uidsOnServer.clear();
1803 uidsOnServer.resize( count() * 2 );
1804 uidsForDeletionOnServer.clear();
1805 mMsgsForDownload.clear();
1806 mUidsForDownload.clear();
1807 // listing is only considered successful if saw a syntactically correct imapdigest
1808 mFoundAnIMAPDigest = false;
1809
1810 CachedImapJob* job = new CachedImapJob( FolderJob::tListMessages, this );
1811 connect( job, TQ_SIGNAL( result(KMail::FolderJob *) ),
1812 this, TQ_SLOT( slotGetLastMessagesResult(KMail::FolderJob *) ) );
1813 job->start();
1814}
1815
1816void KMFolderCachedImap::slotGetLastMessagesResult(KMail::FolderJob *job)
1817{
1818 getMessagesResult(job, true);
1819}
1820
1821// Connected to the listMessages job in CachedImapJob
1822void KMFolderCachedImap::slotGetMessagesData(TDEIO::Job * job, const TQByteArray & data)
1823{
1824 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
1825 if ( it == mAccount->jobsEnd() ) { // Shouldn't happen
1826 kdDebug(5006) << "could not find job!?!?!" << endl;
1827 // be sure to reset the sync state, if the listing was partial we would
1828 // otherwise delete not-listed mail locally, and on the next sync on the server
1829 // as well
1830 mSyncState = SYNC_STATE_HANDLE_INBOX;
1831 serverSyncInternal(); /* HACK^W Fix: we should at least try to keep going */
1832 return;
1833 }
1834 (*it).cdata += TQCString(data, data.size() + 1);
1835 int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
1836 if (pos > 0) {
1837 int a = (*it).cdata.find("\r\nX-uidValidity:");
1838 if (a != -1) {
1839 int b = (*it).cdata.find("\r\n", a + 17);
1840 setUidValidity((*it).cdata.mid(a + 17, b - a - 17));
1841 }
1842 a = (*it).cdata.find("\r\nX-Access:");
1843 // Only trust X-Access (i.e. the imap select info) if we don't know mUserRights.
1844 // The latter is more accurate (checked on every sync) whereas X-Access is only
1845 // updated when selecting the folder again, which might not happen if using
1846 // RMB / Check Mail in this folder. We don't need two (potentially conflicting)
1847 // sources for the readonly setting, in any case.
1848 if (a != -1 && mUserRightsState != KMail::ACLJobs::Ok ) {
1849 int b = (*it).cdata.find("\r\n", a + 12);
1850 const TQString access = (*it).cdata.mid(a + 12, b - a - 12);
1851 setReadOnly( access == "Read only" );
1852 }
1853 (*it).cdata.remove(0, pos);
1854 mFoundAnIMAPDigest = true;
1855 }
1856 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
1857 // Start with something largish when rebuilding the cache
1858 if ( uidsOnServer.size() == 0 )
1859 uidsOnServer.resize( KMail::nextPrime( 2000 ) );
1860 const int v = 42;
1861 while (pos >= 0) {
1862 /*
1863 KMMessage msg;
1864 msg.fromString((*it).cdata.mid(16, pos - 16));
1865 const int flags = msg.headerField("X-Flags").toInt();
1866 const ulong size = msg.headerField("X-Length").toULong();
1867 const ulong uid = msg.UID();
1868 */
1869 // The below is optimized for speed, not prettiness. The commented out chunk
1870 // above was the solution copied from kmfolderimap, and it's 15-20% slower.
1871 const TQCString& entry( (*it).cdata );
1872 const int indexOfUID = entry.find("X-UID", 16);
1873 const int startOfUIDValue = indexOfUID + 7;
1874 const int indexOfLength = entry.find("X-Length", startOfUIDValue ); // we know length comes after UID
1875 const int startOfLengthValue = indexOfLength + 10;
1876 const int indexOfFlags = entry.find("X-Flags", startOfLengthValue ); // we know flags comes last
1877 const int startOfFlagsValue = indexOfFlags + 9;
1878
1879 const int flags = entry.mid( startOfFlagsValue, entry.find( '\r', startOfFlagsValue ) - startOfFlagsValue ).toInt();
1880 const ulong size = entry.mid( startOfLengthValue, entry.find( '\r', startOfLengthValue ) - startOfLengthValue ).toULong();
1881 const ulong uid = entry.mid( startOfUIDValue, entry.find( '\r', startOfUIDValue ) - startOfUIDValue ).toULong();
1882
1883 const bool deleted = ( flags & 8 );
1884 if ( !deleted ) {
1885 if( uid != 0 ) {
1886 if ( uidsOnServer.count() == uidsOnServer.size() ) {
1887 uidsOnServer.resize( KMail::nextPrime( uidsOnServer.size() * 2 ) );
1888 //kdDebug( 5006 ) << "Resizing to: " << uidsOnServer.size() << endl;
1889 }
1890 uidsOnServer.insert( uid, &v );
1891 }
1892 bool redownload = false;
1893 if ( uid <= lastUid() ) {
1894 /*
1895 * If this message UID is not present locally, then it must
1896 * have been deleted by the user, so we delete it on the
1897 * server also. If we don't have delete permissions on the server,
1898 * re-download the message, it must have vanished by some error, or
1899 * while we still thought we were allowed to delete (ACL change).
1900 *
1901 * This relies heavily on lastUid() being correct at all times.
1902 */
1903 // kdDebug(5006) << "KMFolderCachedImap::slotGetMessagesData() : folder "<<label()<<" already has msg="<<msg->headerField("Subject") << ", UID="<<uid << ", lastUid = " << mLastUid << endl;
1904 KMMsgBase *existingMessage = findByUID(uid);
1905 if( !existingMessage ) {
1906#if MAIL_LOSS_DEBUGGING
1907 kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should delete it" << endl;
1908#endif
1909 // double check we deleted it since the last sync
1910 if ( mDeletedUIDsSinceLastSync.contains(uid) ) {
1911 if ( mUserRightsState != KMail::ACLJobs::Ok || ( mUserRights & KMail::ACLJobs::Delete ) ) {
1912#if MAIL_LOSS_DEBUGGING
1913 kdDebug(5006) << "message with uid " << uid << " is gone from local cache. Must be deleted on server!!!" << endl;
1914#endif
1915 uidsForDeletionOnServer << uid;
1916 } else {
1917 redownload = true;
1918 }
1919 } else {
1920 kdDebug(5006) << "WARNING: ####### " << endl;
1921 kdDebug(5006) << "Message locally missing but not deleted in folder: " << folder()->prettyURL() << endl;
1922 kdDebug(5006) << "The missing UID: " << uid << ". It will be redownloaded " << endl;
1923 redownload = true;
1924 }
1925
1926 } else {
1927 // if this is a read only folder, ignore status updates from the server
1928 // since we can't write our status back our local version is what has to
1929 // be considered correct.
1930 if ( !mReadOnly || !GlobalSettings::allowLocalFlags() ) {
1931 /* The message is OK, update flags */
1932 KMFolderImap::flagsToStatus( existingMessage, flags, false, mReadOnly ? INT_MAX : mPermanentFlags );
1933 } else if ( mUserRights & KMail::ACLJobs::WriteSeenFlag ) {
1934 KMFolderImap::seenFlagToStatus( existingMessage, flags );
1935 }
1936 }
1937 // kdDebug(5006) << "message with uid " << uid << " found in the local cache. " << endl;
1938 }
1939 if ( uid > lastUid() || redownload ) {
1940#if MAIL_LOSS_DEBUGGING
1941 kdDebug(5006) << "Looking at uid " << uid << " high water is: " << lastUid() << " we should download it" << endl;
1942#endif
1943 // The message is new since the last sync, but we might have just uploaded it, in which case
1944 // the uid map already contains it.
1945 if ( !uidMap.contains( uid ) ) {
1946 mMsgsForDownload << KMail::CachedImapJob::MsgForDownload(uid, flags, size);
1947 if( imapPath() == "/INBOX/" )
1948 mUidsForDownload << uid;
1949 }
1950 // Remember the highest uid and once the download is completed, update mLastUid
1951 if ( uid > mTentativeHighestUid ) {
1952#if MAIL_LOSS_DEBUGGING
1953 kdDebug(5006) << "Setting the tentative highest UID to: " << uid << endl;
1954#endif
1955 mTentativeHighestUid = uid;
1956 }
1957 }
1958 }
1959 (*it).cdata.remove(0, pos);
1960 (*it).done++;
1961 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
1962 }
1963}
1964
1965void KMFolderCachedImap::getMessagesResult( KMail::FolderJob *job, bool lastSet )
1966{
1967 mProgress += 10;
1968 if ( !job->error() && !mFoundAnIMAPDigest ) {
1969 kdWarning(5006) << "######## Folderlisting did not complete, but there was no error! "
1970 "Aborting sync of folder: " << folder()->prettyURL() << endl;
1971#if MAIL_LOSS_DEBUGGING
1972 kmkernel->emergencyExit( i18n("Folder listing failed in interesting ways." ) );
1973#endif
1974 }
1975 if( job->error() ) { // error listing messages but the user chose to continue
1976 mContentState = imapNoInformation;
1977 mSyncState = SYNC_STATE_HANDLE_INBOX; // be sure not to continue in this folder
1978 } else {
1979 if( lastSet ) { // always true here (this comes from online-imap...)
1980 mContentState = imapFinished;
1981 mUIDsOfLocallyChangedStatuses.clear(); // we are up to date again
1982 mStatusChangedLocally = false;
1983 }
1984 }
1985 serverSyncInternal();
1986}
1987
1988void KMFolderCachedImap::slotProgress(unsigned long done, unsigned long total)
1989{
1990 int progressSpan = 100 - 5 - mProgress;
1991 int additionalProgress = ( total == 0 ) ?
1992 progressSpan :
1993 ( progressSpan * done ) / total;
1994
1995 // Progress info while retrieving new emails
1996 // (going from mProgress to mProgress+progressSpan)
1997 newState( mProgress + additionalProgress, TQString() );
1998}
1999
2000void KMFolderCachedImap::setAccount(KMAcctCachedImap *aAccount)
2001{
2002 assert( aAccount->isA("KMAcctCachedImap") );
2003 mAccount = aAccount;
2004 if( imapPath()=="/" ) aAccount->setFolder( folder() );
2005
2006 // Folder was renamed in a previous session, and the user didn't sync yet
2007 TQString newName = mAccount->renamedFolder( imapPath() );
2008 if ( !newName.isEmpty() )
2009 folder()->setLabel( newName );
2010
2011 if( !folder() || !folder()->child() || !folder()->child()->count() ) return;
2012 for( KMFolderNode* node = folder()->child()->first(); node;
2013 node = folder()->child()->next() )
2014 if (!node->isDir())
2015 static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
2016}
2017
2018void KMFolderCachedImap::listNamespaces()
2019{
2020 ImapAccountBase::ListType type = ImapAccountBase::List;
2021 if ( mAccount->onlySubscribedFolders() )
2022 type = ImapAccountBase::ListSubscribed;
2023
2024 kdDebug(5006) << "listNamespaces " << mNamespacesToList << endl;
2025 if ( mNamespacesToList.isEmpty() ) {
2026 mSyncState = SYNC_STATE_DELETE_SUBFOLDERS;
2027 mPersonalNamespacesCheckDone = true;
2028
2029 TQStringList ns = mAccount->namespaces()[ImapAccountBase::OtherUsersNS];
2030 ns += mAccount->namespaces()[ImapAccountBase::SharedNS];
2031 mNamespacesToCheck = ns.count();
2032 for ( TQStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
2033 {
2034 if ( (*it).isEmpty() ) {
2035 // ignore empty listings as they have been listed before
2036 --mNamespacesToCheck;
2037 continue;
2038 }
2039 KMail::ListJob* job = new KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) );
2040 job->setHonorLocalSubscription( true );
2041 connect( job, TQ_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
2042 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
2043 this, TQ_SLOT(slotCheckNamespace(const TQStringList&, const TQStringList&,
2044 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
2045 job->start();
2046 }
2047 if ( mNamespacesToCheck == 0 ) {
2048 serverSyncInternal();
2049 }
2050 return;
2051 }
2052 mPersonalNamespacesCheckDone = false;
2053
2054 TQString ns = mNamespacesToList.front();
2055 mNamespacesToList.pop_front();
2056
2057 mSyncState = SYNC_STATE_LIST_SUBFOLDERS2;
2058 newState( mProgress, i18n("Retrieving folders for namespace %1").arg(ns));
2059 KMail::ListJob* job = new KMail::ListJob( mAccount, type, this,
2060 mAccount->addPathToNamespace( ns ) );
2061 job->setNamespace( ns );
2062 job->setHonorLocalSubscription( true );
2063 connect( job, TQ_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
2064 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
2065 this, TQ_SLOT(slotListResult(const TQStringList&, const TQStringList&,
2066 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
2067 job->start();
2068}
2069
2070void KMFolderCachedImap::slotCheckNamespace( const TQStringList& subfolderNames,
2071 const TQStringList& subfolderPaths,
2072 const TQStringList& subfolderMimeTypes,
2073 const TQStringList& subfolderAttributes,
2074 const ImapAccountBase::jobData& jobData )
2075{
2076 Q_UNUSED( subfolderPaths );
2077 Q_UNUSED( subfolderMimeTypes );
2078 Q_UNUSED( subfolderAttributes );
2079 --mNamespacesToCheck;
2080 kdDebug(5006) << "slotCheckNamespace " << subfolderNames << ",remain=" <<
2081 mNamespacesToCheck << endl;
2082
2083 // get a correct foldername:
2084 // strip / and make sure it does not contain the delimiter
2085 TQString name = jobData.path.mid( 1, jobData.path.length()-2 );
2086 name.remove( mAccount->delimiterForNamespace( name ) );
2087 if ( name.isEmpty() ) {
2088 // should not happen
2089 kdWarning(5006) << "slotCheckNamespace: ignoring empty folder!" << endl;
2090 return;
2091 }
2092
2093 folder()->createChildFolder();
2094 KMFolderNode *node = 0;
2095 for ( node = folder()->child()->first(); node;
2096 node = folder()->child()->next())
2097 {
2098 if ( !node->isDir() && node->name() == name )
2099 break;
2100 }
2101 if ( !subfolderNames.isEmpty() ) {
2102 if ( node ) {
2103 // folder exists so we have nothing to do - it will be listed later
2104 kdDebug(5006) << "found namespace folder " << name << endl;
2105 } else
2106 {
2107 // create folder
2108 kdDebug(5006) << "create namespace folder " << name << endl;
2109 KMFolder* newFolder = folder()->child()->createFolder( name, false,
2110 KMFolderTypeCachedImap );
2111 if ( newFolder ) {
2112 KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>( newFolder->storage() );
2113 f->setImapPath( mAccount->addPathToNamespace( name ) );
2114 f->setNoContent( true );
2115 f->setAccount( mAccount );
2116 f->close("cachedimap");
2117 kmkernel->dimapFolderMgr()->contentsChanged();
2118 }
2119 }
2120 } else {
2121 if ( node ) {
2122 kdDebug(5006) << "delete namespace folder " << name << endl;
2123 KMFolder* fld = static_cast<KMFolder*>(node);
2124 kmkernel->dimapFolderMgr()->remove( fld );
2125 }
2126 }
2127
2128 if ( mNamespacesToCheck == 0 ) {
2129 // all namespaces are done so continue with the next step
2130 serverSyncInternal();
2131 }
2132}
2133
2134// This lists the subfolders on the server
2135// and (in slotListResult) takes care of folders that have been removed on the server
2136bool KMFolderCachedImap::listDirectory()
2137{
2138 if( !mAccount->slave() ) { // sync aborted
2139 resetSyncState();
2140 emit folderComplete( this, false );
2141 return false;
2142 }
2143 mSubfolderState = imapInProgress;
2144
2145 // get the folders
2146 ImapAccountBase::ListType type = ImapAccountBase::List;
2147 if ( mAccount->onlySubscribedFolders() )
2148 type = ImapAccountBase::ListSubscribed;
2149 KMail::ListJob* job = new KMail::ListJob( mAccount, type, this );
2150 job->setHonorLocalSubscription( true );
2151 connect( job, TQ_SIGNAL(receivedFolders(const TQStringList&, const TQStringList&,
2152 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)),
2153 this, TQ_SLOT(slotListResult(const TQStringList&, const TQStringList&,
2154 const TQStringList&, const TQStringList&, const ImapAccountBase::jobData&)));
2155 job->start();
2156
2157 return true;
2158}
2159
2160void KMFolderCachedImap::slotListResult( const TQStringList& folderNames,
2161 const TQStringList& folderPaths,
2162 const TQStringList& folderMimeTypes,
2163 const TQStringList& folderAttributes,
2164 const ImapAccountBase::jobData& jobData )
2165{
2166 Q_UNUSED( jobData );
2167 //kdDebug(5006) << label() << ": folderNames=" << folderNames << " folderPaths="
2168 //<< folderPaths << " mimeTypes=" << folderMimeTypes << endl;
2169 mSubfolderNames = folderNames;
2170 mSubfolderPaths = folderPaths;
2171 mSubfolderMimeTypes = folderMimeTypes;
2172 mSubfolderState = imapFinished;
2173 mSubfolderAttributes = folderAttributes;
2174 //kdDebug(5006) << "##### setting subfolder attributes: " << mSubfolderAttributes << endl;
2175
2176 folder()->createChildFolder();
2177 KMFolderNode *node = folder()->child()->first();
2178 bool root = ( this == mAccount->rootFolder() );
2179
2180 TQPtrList<KMFolder> toRemove;
2181 bool emptyList = ( root && mSubfolderNames.empty() );
2182 if ( !emptyList ) {
2183 while (node) {
2184 if (!node->isDir() ) {
2185 KMFolderCachedImap *f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
2186
2187 if ( mSubfolderNames.findIndex(node->name()) == -1 ) {
2188 TQString name = node->name();
2189 // as more than one namespace can be listed in the root folder we need to make sure
2190 // that the folder is within the current namespace
2191 bool isInNamespace = ( jobData.curNamespace.isEmpty() ||
2192 jobData.curNamespace == mAccount->namespaceForFolder( f ) );
2193 // ignore some cases
2194 bool ignore = root && ( f->imapPath() == "/INBOX/" ||
2195 mAccount->isNamespaceFolder( name ) || !isInNamespace );
2196
2197 // This subfolder isn't present on the server
2198 if( !f->imapPath().isEmpty() && !ignore ) {
2199 // The folder has an imap path set, so it has been
2200 // on the server before. Delete it locally.
2201 toRemove.append( f->folder() );
2202 kdDebug(5006) << node->name() << " isn't on the server. It has an imapPath -> delete it locally" << endl;
2203 }
2204 } else { // folder both local and on server
2205 //kdDebug(5006) << node->name() << " is on the server." << endl;
2206
2210 int index = mSubfolderNames.findIndex( node->name() );
2211 f->mFolderAttributes = folderAttributes[ index ];
2212 }
2213 } else {
2214 //kdDebug(5006) << "skipping dir node:" << node->name() << endl;
2215 }
2216 node = folder()->child()->next();
2217 }
2218 }
2219
2220 for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() ) {
2221 rescueUnsyncedMessagesAndDeleteFolder( doomed );
2222 }
2223
2224 mProgress += 5;
2225
2226 // just in case there is nothing to rescue
2227 slotRescueDone( 0 );
2228}
2229
2230// This synchronizes the local folders as needed (creation/deletion). No network communication here.
2231void KMFolderCachedImap::listDirectory2()
2232{
2233 TQString path = folder()->path();
2234 kmkernel->dimapFolderMgr()->quiet(true);
2235
2236 bool root = ( this == mAccount->rootFolder() );
2237 if ( root && !mAccount->hasInbox() )
2238 {
2239 KMFolderCachedImap *f = 0;
2240 KMFolderNode *node;
2241 // create the INBOX
2242 for (node = folder()->child()->first(); node; node = folder()->child()->next())
2243 if (!node->isDir() && node->name() == "INBOX") break;
2244 if (node) {
2245 f = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
2246 } else {
2247 KMFolder* newFolder = folder()->child()->createFolder("INBOX", true, KMFolderTypeCachedImap);
2248 if ( newFolder ) {
2249 f = static_cast<KMFolderCachedImap*>(newFolder->storage());
2250 }
2251 }
2252 if ( f ) {
2253 f->setAccount( mAccount );
2254 f->setImapPath( "/INBOX/" );
2255 f->folder()->setLabel( i18n("inbox") );
2256 }
2257 if (!node) {
2258 if ( f )
2259 f->close("cachedimap");
2260 kmkernel->dimapFolderMgr()->contentsChanged();
2261 }
2262 // so we have an INBOX
2263 mAccount->setHasInbox( true );
2264 }
2265
2266 if ( root && !mSubfolderNames.isEmpty() ) {
2267 KMFolderCachedImap* parent =
2268 findParent( mSubfolderPaths.first(), mSubfolderNames.first() );
2269 if ( parent ) {
2270 kdDebug(5006) << "KMFolderCachedImap::listDirectory2 - pass listing to "
2271 << parent->label() << endl;
2272 mSubfolderNames.clear();
2273 }
2274 }
2275
2276 // Find all subfolders present on server but not on disk
2277 TQValueVector<int> foldersNewOnServer;
2278 for (uint i = 0; i < mSubfolderNames.count(); i++) {
2279
2280 // Find the subdir, if already present
2281 KMFolderCachedImap *f = 0;
2282 KMFolderNode *node = 0;
2283 for (node = folder()->child()->first(); node;
2284 node = folder()->child()->next())
2285 if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
2286
2287 if (!node) {
2288 // This folder is not present here
2289 // Either it's new on the server, or we just deleted it.
2290 TQString subfolderPath = mSubfolderPaths[i];
2291 // The code used to look at the uidcache to know if it was "just deleted".
2292 // But this breaks with noContent folders and with shared folders.
2293 // So instead we keep a list in the account.
2294 bool locallyDeleted = mAccount->isDeletedFolder( subfolderPath );
2295 // That list is saved/restored across sessions, but to avoid any mistake,
2296 // ask for confirmation if the folder was deleted in a previous session
2297 // (could be that the folder was deleted & recreated meanwhile from another client...)
2298 if ( !locallyDeleted && mAccount->isPreviouslyDeletedFolder( subfolderPath ) ) {
2299 locallyDeleted = KMessageBox::warningYesNo(
2300 0, i18n( "<qt><p>It seems that the folder <b>%1</b> was deleted. Do you want to delete it from the server?</p></qt>" ).arg( mSubfolderNames[i] ), TQString(), KStdGuiItem::del(), KStdGuiItem::cancel() ) == KMessageBox::Yes;
2301 }
2302
2303 if ( locallyDeleted ) {
2304 kdDebug(5006) << subfolderPath << " was deleted locally => delete on server." << endl;
2305 foldersForDeletionOnServer += mAccount->deletedFolderPaths( subfolderPath ); // grab all subsubfolders too
2306 } else {
2307 kdDebug(5006) << subfolderPath << " is a new folder on the server => create local cache" << endl;
2308 foldersNewOnServer.append( i );
2309 }
2310 } else { // Folder found locally
2311 if( static_cast<KMFolder*>(node)->folderType() == KMFolderTypeCachedImap )
2312 f = dynamic_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
2313 if( f ) {
2314 // kdDebug(5006) << "folder("<<f->name()<<")->imapPath()=" << f->imapPath()
2315 // << "\nSetting imapPath " << mSubfolderPaths[i] << endl;
2316 // Write folder settings
2317 f->setAccount(mAccount);
2318 f->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
2319 f->setNoChildren(mSubfolderMimeTypes[i] == "message/digest");
2320 f->setImapPath(mSubfolderPaths[i]);
2321 }
2322 }
2323 }
2324
2325 /* In case we are ignoring non-groupware folders, and this is the groupware
2326 * main account, find out the contents types of folders that have newly
2327 * appeared on the server. Otherwise just create them and finish listing.
2328 * If a folder is already known to be locally unsubscribed, it won't be
2329 * listed at all, on this level, so these are only folders that we are
2330 * seeing for the first time. */
2331
2332 /* Note: We ask the globalsettings, and not the current state of the
2333 * kmkernel->iCalIface().isEnabled(), since that is false during the
2334 * very first sync, where we already want to filter. */
2335 if ( GlobalSettings::self()->showOnlyGroupwareFoldersForGroupwareAccount()
2336 && GlobalSettings::self()->theIMAPResourceAccount() == (int)mAccount->id()
2337 && mAccount->hasAnnotationSupport()
2338 && GlobalSettings::self()->theIMAPResourceEnabled()
2339 && !foldersNewOnServer.isEmpty() ) {
2340
2341 TQStringList paths;
2342 for ( uint i = 0; i < foldersNewOnServer.count(); ++i )
2343 paths << mSubfolderPaths[ foldersNewOnServer[i] ];
2344
2346 AnnotationJobs::multiUrlGetAnnotation( mAccount->slave(), mAccount->getUrl(), paths, KOLAB_FOLDERTYPE );
2347 ImapAccountBase::jobData jd( TQString(), folder() );
2348 jd.cancellable = true;
2349 mAccount->insertJob(job, jd);
2350 connect( job, TQ_SIGNAL(result(TDEIO::Job *)),
2351 TQ_SLOT(slotMultiUrlGetAnnotationResult(TDEIO::Job *)) );
2352
2353 } else {
2354 createFoldersNewOnServerAndFinishListing( foldersNewOnServer );
2355 }
2356}
2357
2358void KMFolderCachedImap::createFoldersNewOnServerAndFinishListing( const TQValueVector<int> foldersNewOnServer )
2359{
2360 for ( uint i = 0; i < foldersNewOnServer.count(); ++i ) {
2361 int idx = foldersNewOnServer[i];
2362 KMFolder* newFolder = folder()->child()->createFolder( mSubfolderNames[idx], false, KMFolderTypeCachedImap);
2363 if (newFolder) {
2364 KMFolderCachedImap *f = dynamic_cast<KMFolderCachedImap*>(newFolder->storage());
2365 kdDebug(5006) << " ####### Locally creating folder " << mSubfolderNames[idx] <<endl;
2366 f->close("cachedimap");
2367 f->setAccount(mAccount);
2368 f->mAnnotationFolderType = "FROMSERVER";
2369 f->setNoContent(mSubfolderMimeTypes[idx] == "inode/directory");
2370 f->setNoChildren(mSubfolderMimeTypes[idx] == "message/digest");
2371 f->setImapPath(mSubfolderPaths[idx]);
2372 f->mFolderAttributes = mSubfolderAttributes[idx];
2373 mNewlyCreatedSubfolders.append( TQGuardedPtr<KMFolderCachedImap>( f ) );
2374 kdDebug(5006) << " ####### Attributes: " << f->mFolderAttributes <<endl;
2375 //kdDebug(5006) << subfolderPath << ": mAnnotationFolderType set to FROMSERVER" << endl;
2376 kmkernel->dimapFolderMgr()->contentsChanged();
2377 } else {
2378 kdDebug(5006) << "can't create folder " << mSubfolderNames[idx] <<endl;
2379 }
2380 }
2381
2382 kmkernel->dimapFolderMgr()->quiet(false);
2383 emit listComplete(this);
2384 if ( !mPersonalNamespacesCheckDone ) {
2385 // we're not done with the namespaces
2386 mSyncState = SYNC_STATE_LIST_NAMESPACES;
2387 }
2388 serverSyncInternal();
2389}
2390
2391//-----------------------------------------------------------------------------
2392KMFolderCachedImap* KMFolderCachedImap::findParent( const TQString& path,
2393 const TQString& name )
2394{
2395 TQString parent = path.left( path.length() - name.length() - 2 );
2396 if ( parent.length() > 1 )
2397 {
2398 // extract name of the parent
2399 parent = parent.right( parent.length() - 1 );
2400 if ( parent != label() )
2401 {
2402 KMFolderNode *node = folder()->child()->first();
2403 // look for a better parent
2404 while ( node )
2405 {
2406 if ( node->name() == parent )
2407 {
2408 KMFolder* fld = static_cast<KMFolder*>(node);
2409 KMFolderCachedImap* imapFld =
2410 static_cast<KMFolderCachedImap*>( fld->storage() );
2411 return imapFld;
2412 }
2413 node = folder()->child()->next();
2414 }
2415 }
2416 }
2417 return 0;
2418}
2419
2420void KMFolderCachedImap::slotSubFolderComplete(KMFolderCachedImap* sub, bool success)
2421{
2422 Q_UNUSED(sub);
2423 //kdDebug(5006) << label() << " slotSubFolderComplete: " << sub->label() << endl;
2424 if ( success ) {
2425 serverSyncInternal();
2426 }
2427 else
2428 {
2429 // success == false means the sync was aborted.
2430 if ( mCurrentSubfolder ) {
2431 Q_ASSERT( sub == mCurrentSubfolder );
2432 disconnectSubFolderSignals();
2433 }
2434
2435 // Next step would be to check quota limits and then to close the folder, but don't bother with
2436 // both and close the folder right here, since we aborted.
2437 mSubfoldersForSync.clear();
2438 mSyncState = SYNC_STATE_INITIAL;
2439 close("cachedimap");
2440 emit syncStateChanged();
2441 emit folderComplete( this, false );
2442 }
2443}
2444
2445void KMFolderCachedImap::slotSubFolderCloseToQuotaChanged()
2446{
2447 if ( !mQuotaOnly ) {
2448 mSomeSubFolderCloseToQuotaChanged = true;
2449 }
2450}
2451
2452void KMFolderCachedImap::slotSimpleData(TDEIO::Job * job, const TQByteArray & data)
2453{
2454 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2455 if (it == mAccount->jobsEnd()) return;
2456 TQBuffer buff((*it).data);
2457 buff.open(IO_WriteOnly | IO_Append);
2458 buff.writeBlock(data.data(), data.size());
2459 buff.close();
2460}
2461
2462FolderJob*
2463KMFolderCachedImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, KMFolder *folder,
2464 TQString, const AttachmentStrategy* ) const
2465{
2466 TQPtrList<KMMessage> msgList;
2467 msgList.append( msg );
2468 CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
2469 job->setParentFolder( this );
2470 return job;
2471}
2472
2473FolderJob*
2474KMFolderCachedImap::doCreateJob( TQPtrList<KMMessage>& msgList, const TQString& sets,
2475 FolderJob::JobType jt, KMFolder *folder ) const
2476{
2477 //FIXME: how to handle sets here?
2478 Q_UNUSED( sets );
2479 CachedImapJob *job = new CachedImapJob( msgList, jt, folder? static_cast<KMFolderCachedImap*>( folder->storage() ):0 );
2480 job->setParentFolder( this );
2481 return job;
2482}
2483
2484void
2485KMFolderCachedImap::setUserRights( unsigned int userRights, KMail::ACLJobs::ACLFetchState state )
2486{
2487 mUserRights = userRights;
2488 mUserRightsState = state;
2489 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2490}
2491
2492void
2493KMFolderCachedImap::slotReceivedUserRights( KMFolder* folder )
2494{
2495 if ( folder->storage() == this ) {
2496 disconnect( mAccount, TQ_SIGNAL( receivedUserRights( KMFolder* ) ),
2497 this, TQ_SLOT( slotReceivedUserRights( KMFolder* ) ) );
2498 if ( mUserRightsState == KMail::ACLJobs::Ok ) {
2499 setReadOnly( ( mUserRights & KMail::ACLJobs::Insert ) == 0 );
2500 }
2501 mProgress += 5;
2502 serverSyncInternal();
2503 }
2504}
2505
2506void
2507KMFolderCachedImap::setReadOnly( bool readOnly )
2508{
2509 if ( readOnly != mReadOnly ) {
2510 mReadOnly = readOnly;
2511 emit readOnlyChanged( folder() );
2512 }
2513}
2514
2515void
2516KMFolderCachedImap::slotReceivedACL( KMFolder* folder, TDEIO::Job* job, const KMail::ACLList& aclList )
2517{
2518 if ( folder->storage() == this ) {
2519 disconnect( mAccount, TQ_SIGNAL(receivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )),
2520 this, TQ_SLOT(slotReceivedACL( KMFolder*, TDEIO::Job*, const KMail::ACLList& )) );
2521 mACLListState = job->error() ? KMail::ACLJobs::FetchFailed : KMail::ACLJobs::Ok;
2522 mACLList = aclList;
2523 serverSyncInternal();
2524 }
2525}
2526
2527void
2528KMFolderCachedImap::slotStorageQuotaResult( const QuotaInfo& info )
2529{
2530 setQuotaInfo( info );
2531}
2532
2533void KMFolderCachedImap::setQuotaInfo( const QuotaInfo & info )
2534{
2535 if ( info != mQuotaInfo ) {
2536 const bool wasCloseToQuota = isCloseToQuota();
2537 mQuotaInfo = info;
2538 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2539 if ( wasCloseToQuota != isCloseToQuota() ) {
2540 emit closeToQuotaChanged();
2541 }
2542 emit folderSizeChanged();
2543 }
2544}
2545
2546void
2547KMFolderCachedImap::setACLList( const ACLList& arr )
2548{
2549 mACLList = arr;
2550 mACLListState = KMail::ACLJobs::Ok;
2551}
2552
2553void
2554KMFolderCachedImap::slotMultiSetACLResult(TDEIO::Job *job)
2555{
2556 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2557 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2558 if ( (*it).parent != folder() ) return; // Shouldn't happen
2559
2560 if ( job->error() )
2561 // Display error but don't abort the sync just for this
2562 // PENDING(dfaure) reconsider using handleJobError now that it offers continue/cancel
2563 job->showErrorDialog();
2564 else
2565 kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
2566
2567 if (mAccount->slave()) mAccount->removeJob(job);
2568 serverSyncInternal();
2569}
2570
2571void
2572KMFolderCachedImap::slotACLChanged( const TQString& userId, int permissions )
2573{
2574 // The job indicates success in changing the permissions for this user
2575 // -> we note that it's been done.
2576 for( ACLList::Iterator it = mACLList.begin(); it != mACLList.end(); ++it ) {
2577 if ( (*it).userId == userId && (*it).permissions == permissions ) {
2578 if ( permissions == -1 ) // deleted
2579 mACLList.erase( it );
2580 else // added/modified
2581 (*it).changed = false;
2582 return;
2583 }
2584 }
2585}
2586
2587// called by KMAcctCachedImap::killAllJobs
2588void KMFolderCachedImap::resetSyncState()
2589{
2590 if ( mSyncState == SYNC_STATE_INITIAL ) return;
2591 mSubfoldersForSync.clear();
2592 mNewlyCreatedSubfolders.clear();
2593 mSyncState = SYNC_STATE_INITIAL;
2594 close("cachedimap");
2595 // Don't use newState here, it would revert to mProgress (which is < current value when listing messages)
2596 KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
2597 TQString str = i18n("Aborted");
2598 if (progressItem)
2599 progressItem->setStatus( str );
2600 emit statusMsg( str );
2601 emit syncStateChanged();
2602}
2603
2604void KMFolderCachedImap::slotIncreaseProgress()
2605{
2606 mProgress += 5;
2607}
2608
2609void KMFolderCachedImap::newState( int progress, const TQString& syncStatus )
2610{
2611 //kdDebug() << k_funcinfo << folder() << " " << mProgress << " " << syncStatus << endl;
2612 KPIM::ProgressItem *progressItem = mAccount->mailCheckProgressItem();
2613 if( progressItem )
2614 progressItem->setCompletedItems( progress );
2615 if ( !syncStatus.isEmpty() ) {
2616 TQString str;
2617 // For a subfolder, show the label. But for the main folder, it's already shown.
2618 if ( mAccount->imapFolder() == this )
2619 str = syncStatus;
2620 else
2621 str = TQString( "%1: %2" ).arg( label() ).arg( syncStatus );
2622 if( progressItem )
2623 progressItem->setStatus( str );
2624 emit statusMsg( str );
2625 }
2626 if( progressItem )
2627 progressItem->updateProgress();
2628}
2629
2630void KMFolderCachedImap::setSubfolderState( imapState state )
2631{
2632 mSubfolderState = state;
2633 if ( state == imapNoInformation && folder()->child() )
2634 {
2635 // pass through to childs
2636 KMFolderNode* node;
2637 TQPtrListIterator<KMFolderNode> it( *folder()->child() );
2638 for ( ; (node = it.current()); )
2639 {
2640 ++it;
2641 if (node->isDir()) continue;
2642 KMFolder *folder = static_cast<KMFolder*>(node);
2643 static_cast<KMFolderCachedImap*>(folder->storage())->setSubfolderState( state );
2644 }
2645 }
2646}
2647
2648void KMFolderCachedImap::setImapPath(const TQString &path)
2649{
2650 mImapPath = path;
2651}
2652
2653static bool isFolderTypeKnownToUs( const TQString &type )
2654{
2655 for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
2656 FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
2657 if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) )
2658 return true;
2659 }
2660 return false;
2661}
2662
2663// mAnnotationFolderType is the annotation as known to the server (and stored in kmailrc)
2664// It is updated from the folder contents type and whether it's a standard resource folder.
2665// This happens during the syncing phase and during initFolder for a new folder.
2666// Don't do it earlier, e.g. from setContentsType:
2667// on startup, it's too early there to know if this is a standard resource folder.
2668void KMFolderCachedImap::updateAnnotationFolderType()
2669{
2670 TQString oldType = mAnnotationFolderType;
2671 TQString oldSubType;
2672 int dot = oldType.find( '.' );
2673 if ( dot != -1 ) {
2674 oldType.truncate( dot );
2675 oldSubType = mAnnotationFolderType.mid( dot + 1 );
2676 }
2677
2678 TQString newType, newSubType;
2679 // We want to store an annotation on the folder only if using the kolab storage.
2680 if ( kmkernel->iCalIface().storageFormat( folder() ) == KMailICalIfaceImpl::StorageXML ) {
2681 newType = KMailICalIfaceImpl::annotationForContentsType( mContentsType );
2682 if ( kmkernel->iCalIface().isStandardResourceFolder( folder() ) )
2683 newSubType = "default";
2684 else if ( oldSubType != "default" )
2685 newSubType = oldSubType; // preserve unknown subtypes, like drafts etc.
2686 }
2687
2688 // We do not want to overwrite custom folder types (which we treat as mail folders).
2689 // So only overwrite custom folder types if the user changed the folder type himself to something
2690 // other than mail.
2691 const bool changingTypeAllowed = isFolderTypeKnownToUs( oldType ) ||
2692 ( mContentsType != ContentsTypeMail );
2693
2694 //kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: " << newType << " " << newSubType << endl;
2695 if ( ( newType != oldType || newSubType != oldSubType ) && changingTypeAllowed ) {
2696 mAnnotationFolderType = newType + ( newSubType.isEmpty() ? TQString() : "."+newSubType );
2697 mAnnotationFolderTypeChanged = true; // force a "set annotation" on next sync
2698 kdDebug(5006) << mImapPath << ": updateAnnotationFolderType: '" << mAnnotationFolderType << "', was (" << oldType << " " << oldSubType << ") => mAnnotationFolderTypeChanged set to TRUE" << endl;
2699 }
2700 // Ensure that further readConfig()s don't lose mAnnotationFolderType
2701 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2702}
2703
2704void KMFolderCachedImap::setIncidencesFor( IncidencesFor incfor )
2705{
2706 if ( mIncidencesFor != incfor ) {
2707 mIncidencesFor = incfor;
2708 mIncidencesForChanged = true;
2709 }
2710}
2711
2712void KMFolderCachedImap::setSharedSeenFlags(bool b)
2713{
2714 if ( mSharedSeenFlags != b ) {
2715 mSharedSeenFlags = b;
2716 mSharedSeenFlagsChanged = true;
2717 }
2718}
2719
2720void KMFolderCachedImap::slotAnnotationResult(const TQString& entry, const TQString& value, bool found)
2721{
2722 if ( entry == KOLAB_FOLDERTYPE ) {
2723 // There are four cases.
2724 // 1) no content-type on server -> set it
2725 // 2) different content-type on server, locally changed -> set it (we don't even come here)
2726 // 3) different (known) content-type on server, no local change -> get it
2727 // 4) different unknown content-type on server, probably some older version -> set it
2728 if ( found ) {
2729 TQString type = value;
2730 TQString subtype;
2731 int dot = value.find( '.' );
2732 if ( dot != -1 ) {
2733 type.truncate( dot );
2734 subtype = value.mid( dot + 1 );
2735 }
2736 bool foundKnownType = false;
2737 for ( uint i = 0 ; i <= ContentsTypeLast; ++i ) {
2738 FolderContentsType contentsType = static_cast<KMail::FolderContentsType>( i );
2739 if ( type == KMailICalIfaceImpl::annotationForContentsType( contentsType ) ) {
2740 // Case 3: known content-type on server, get it
2741 //kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: found known type of annotation" << endl;
2742 if ( contentsType != ContentsTypeMail )
2743 kmkernel->iCalIface().setStorageFormat( folder(), KMailICalIfaceImpl::StorageXML );
2744 mAnnotationFolderType = value;
2745 if ( folder()->parent()->owner()->idString() != GlobalSettings::self()->theIMAPResourceFolderParent()
2746 && GlobalSettings::self()->theIMAPResourceEnabled()
2747 && subtype == "default" ) {
2748 // Truncate subtype if this folder can't be a default resource folder for us,
2749 // although it apparently is for someone else.
2750 mAnnotationFolderType = type;
2751 kdDebug(5006) << mImapPath << ": slotGetAnnotationResult: parent folder is " << folder()->parent()->owner()->idString() << " => truncating annotation to " << value << endl;
2752 }
2753 setContentsType( contentsType );
2754 mAnnotationFolderTypeChanged = false; // we changed it, not the user
2755 foundKnownType = true;
2756
2757 // Users don't read events/contacts/etc. in kmail, so mark them all as read.
2758 // This is done in cachedimapjob when getting new messages, but do it here too,
2759 // for the initial set of messages when we didn't know this was a resource folder yet,
2760 // for old folders, etc.
2761 if ( contentsType != ContentsTypeMail )
2762 markUnreadAsRead();
2763
2764 break;
2765 }
2766 }
2767 if ( !foundKnownType ) {
2768 //kdDebug(5006) << "slotGetAnnotationResult: no known type of annotation found, leaving it untouched" << endl;
2769
2770 // Case 4: Server has strange content-type. We must not overwrite it, see https://issues.kolab.org/issue2069.
2771 // Treat the content-type as mail until we change it ourselves.
2772 mAnnotationFolderTypeChanged = false;
2773 mAnnotationFolderType = value;
2774 setContentsType( ContentsTypeMail );
2775 }
2776
2777 // Ensure that further readConfig()s don't lose mAnnotationFolderType
2778 writeConfigKeysWhichShouldNotGetOverwrittenByReadConfig();
2779 // TODO handle subtype (inbox, drafts, sentitems, junkemail)
2780 }
2781 else if ( !mReadOnly ) {
2782 // Case 1: server doesn't have content-type, set it
2783 //kdDebug(5006) << "slotGetAnnotationResult: no annotation found, will need to set it" << endl;
2784 mAnnotationFolderTypeChanged = true;
2785 }
2786 } else if ( entry == KOLAB_INCIDENCESFOR ) {
2787 if ( found ) {
2788 mIncidencesFor = incidencesForFromString( value );
2789 Q_ASSERT( mIncidencesForChanged == false );
2790 }
2791 } else if ( entry == KOLAB_SHAREDSEEN ) {
2792 if ( found ) {
2793 mSharedSeenFlags = value == "true";
2794 }
2795 }
2796}
2797
2798void KMFolderCachedImap::slotGetAnnotationResult( TDEIO::Job* job )
2799{
2800 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2801 Q_ASSERT( it != mAccount->jobsEnd() );
2802 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2803 Q_ASSERT( (*it).parent == folder() );
2804 if ( (*it).parent != folder() ) return; // Shouldn't happen
2805
2807 if ( annjob->error() ) {
2808 if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) {
2809 // that's when the imap server doesn't support annotations
2810 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
2811 && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
2812 KMessageBox::error( 0, i18n( "The IMAP server %1 does not have support for IMAP annotations. The XML storage cannot be used on this server; please re-configure KMail differently." ).arg( mAccount->host() ) );
2813 mAccount->setHasNoAnnotationSupport();
2814 }
2815 else
2816 kdWarning(5006) << "slotGetAnnotationResult: " << job->errorString() << endl;
2817 }
2818
2819 if (mAccount->slave()) mAccount->removeJob(job);
2820 mProgress += 2;
2821 serverSyncInternal();
2822}
2823
2824void KMFolderCachedImap::slotMultiUrlGetAnnotationResult( TDEIO::Job* job )
2825{
2826 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2827 Q_ASSERT( it != mAccount->jobsEnd() );
2828 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2829 Q_ASSERT( (*it).parent == folder() );
2830 if ( (*it).parent != folder() ) return; // Shouldn't happen
2831
2832 TQValueVector<int> folders;
2834 = static_cast<AnnotationJobs::MultiUrlGetAnnotationJob *>( job );
2835 if ( annjob->error() ) {
2836 if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) {
2837 // that's when the imap server doesn't support annotations
2838 if ( GlobalSettings::self()->theIMAPResourceStorageFormat() == GlobalSettings::EnumTheIMAPResourceStorageFormat::XML
2839 && (uint)GlobalSettings::self()->theIMAPResourceAccount() == mAccount->id() )
2840 KMessageBox::error( 0, i18n( "The IMAP server %1 doesn't have support for imap annotations. The XML storage cannot be used on this server, please re-configure KMail differently" ).arg( mAccount->host() ) );
2841 mAccount->setHasNoAnnotationSupport();
2842 }
2843 else
2844 kdWarning(5006) << "slotGetMultiUrlAnnotationResult: " << job->errorString() << endl;
2845 } else {
2846 // we got the annotation allright, let's filter out the ones with the wrong type
2847 TQMap<TQString, TQString> annotations = annjob->annotations();
2848 TQMap<TQString, TQString>::Iterator it = annotations.begin();
2849 for ( ; it != annotations.end(); ++it ) {
2850 const TQString folderPath = it.key();
2851 const TQString annotation = it.data();
2852 kdDebug(5006) << k_funcinfo << "Folder: " << folderPath << " has type: " << annotation << endl;
2853 // we're only interested in the main type
2854 TQString type(annotation);
2855 int dot = annotation.find( '.' );
2856 if ( dot != -1 ) type.truncate( dot );
2857 type = type.simplifyWhiteSpace();
2858
2859 const int idx = mSubfolderPaths.findIndex( folderPath );
2860 const bool isNoContent = mSubfolderMimeTypes[idx] == "inode/directory";
2861 if ( ( isNoContent && type.isEmpty() )
2862 || ( !type.isEmpty() && type != KMailICalIfaceImpl::annotationForContentsType( ContentsTypeMail ) ) ) {
2863 folders.append( idx );
2864 kdDebug(5006) << k_funcinfo << " subscribing to: " << folderPath << endl;
2865 } else {
2866 kdDebug(5006) << k_funcinfo << " automatically unsubscribing from: " << folderPath << endl;
2867 mAccount->changeLocalSubscription( folderPath, false );
2868 }
2869 }
2870 }
2871
2872 if (mAccount->slave()) mAccount->removeJob(job);
2873 createFoldersNewOnServerAndFinishListing( folders );
2874}
2875
2876void KMFolderCachedImap::slotQuotaResult( TDEIO::Job* job )
2877{
2878 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2879 Q_ASSERT( it != mAccount->jobsEnd() );
2880 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2881 Q_ASSERT( (*it).parent == folder() );
2882 if ( (*it).parent != folder() ) return; // Shouldn't happen
2883
2884 QuotaJobs::GetStorageQuotaJob* quotajob = static_cast<QuotaJobs::GetStorageQuotaJob *>( job );
2885 QuotaInfo empty;
2886 if ( quotajob->error() ) {
2887 if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION ) {
2888 // that's when the imap server doesn't support quota
2889 mAccount->setHasNoQuotaSupport();
2890 setQuotaInfo( empty );
2891 }
2892 else
2893 kdWarning(5006) << "slotGetQuotaResult: " << job->errorString() << endl;
2894 }
2895
2896 if (mAccount->slave()) mAccount->removeJob(job);
2897 mProgress += 2;
2898 serverSyncInternal();
2899}
2900
2901void
2902KMFolderCachedImap::slotAnnotationChanged( const TQString& entry, const TQString& attribute, const TQString& value )
2903{
2904 Q_UNUSED( attribute );
2905 Q_UNUSED( value );
2906 //kdDebug(5006) << k_funcinfo << entry << " " << attribute << " " << value << endl;
2907 if ( entry == KOLAB_FOLDERTYPE )
2908 mAnnotationFolderTypeChanged = false;
2909 else if ( entry == KOLAB_INCIDENCESFOR ) {
2910 mIncidencesForChanged = false;
2911 // The incidences-for changed, we must trigger the freebusy creation.
2912 // HACK: in theory we would need a new enum value for this.
2913 kmkernel->iCalIface().addFolderChange( folder(), KMailICalIfaceImpl::ACL );
2914 } else if ( entry == KOLAB_SHAREDSEEN ) {
2915 mSharedSeenFlagsChanged = false;
2916 }
2917}
2918
2919void KMFolderCachedImap::slotTestAnnotationResult(TDEIO::Job *job)
2920{
2921 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2922 Q_ASSERT( it != mAccount->jobsEnd() );
2923 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2924 Q_ASSERT( (*it).parent == folder() );
2925 if ( (*it).parent != folder() ) return; // Shouldn't happen
2926
2927 mAccount->setAnnotationCheckPassed( true );
2928 if ( job->error() ) {
2929 kdDebug(5006) << "Test Annotation was not passed, disabling annotation support" << endl;
2930 mAccount->setHasNoAnnotationSupport( );
2931 } else {
2932 kdDebug(5006) << "Test Annotation was passed OK" << endl;
2933 }
2934 if (mAccount->slave()) mAccount->removeJob(job);
2935 serverSyncInternal();
2936}
2937
2938void
2939KMFolderCachedImap::slotSetAnnotationResult(TDEIO::Job *job)
2940{
2941 KMAcctCachedImap::JobIterator it = mAccount->findJob(job);
2942 if ( it == mAccount->jobsEnd() ) return; // Shouldn't happen
2943 if ( (*it).parent != folder() ) return; // Shouldn't happen
2944
2945 bool cont = true;
2946 if ( job->error() ) {
2947 // Don't show error if the server doesn't support ANNOTATEMORE and this folder only contains mail
2948 if ( job->error() == TDEIO::ERR_UNSUPPORTED_ACTION && contentsType() == ContentsTypeMail ) {
2949 if (mAccount->slave()) mAccount->removeJob(job);
2950 } else {
2951 cont = mAccount->handleJobError( job, i18n( "Error while setting annotation: " ) + '\n' );
2952 }
2953 } else {
2954 if (mAccount->slave()) mAccount->removeJob(job);
2955 }
2956 if ( cont )
2957 serverSyncInternal();
2958}
2959
2960void KMFolderCachedImap::slotUpdateLastUid()
2961{
2962 if( mTentativeHighestUid != 0 ) {
2963
2964 // Sanity checking:
2965 // By now all new mails should be downloaded, which means
2966 // that iteration over the folder should yield only UIDs
2967 // lower or equal to what we think the highes ist, and the
2968 // highest one as well. If not, our notion of the highest
2969 // uid we've seen thus far is wrong, which is dangerous, so
2970 // don't update the mLastUid, then.
2971 // Not entirely true though, mails might have been moved out
2972 // of the folder already by filters, thus giving us a higher tentative
2973 // uid than we actually observe here.
2974 bool sane = count() == 0;
2975
2976 for (int i=0;i<count(); i++ ) {
2977 ulong uid = getMsgBase(i)->UID();
2978 if ( uid > mTentativeHighestUid && uid > lastUid() ) {
2979 kdWarning(5006) << "DANGER: Either the server listed a wrong highest uid, "
2980 "or we parsed it wrong. Send email to adam@kde.org, please, and include this log." << endl;
2981 kdWarning(5006) << "uid: " << uid << " mTentativeHighestUid: " << mTentativeHighestUid << endl;
2982 assert( false );
2983 break;
2984 } else {
2985 sane = true;
2986 }
2987 }
2988 if (sane) {
2989#if MAIL_LOSS_DEBUGGING
2990 kdDebug(5006) << "Tentative highest UID test was sane, writing out: " << mTentativeHighestUid << endl;
2991#endif
2992 setLastUid( mTentativeHighestUid );
2993 }
2994 }
2995 mTentativeHighestUid = 0;
2996}
2997
2998bool KMFolderCachedImap::isMoveable() const
2999{
3000 return ( hasChildren() == HasNoChildren &&
3001 !folder()->isSystemFolder() ) ? true : false;
3002}
3003
3004void KMFolderCachedImap::slotFolderDeletionOnServerFinished()
3005{
3006 for ( TQStringList::const_iterator it = foldersForDeletionOnServer.constBegin();
3007 it != foldersForDeletionOnServer.constEnd(); ++it ) {
3008 KURL url( mAccount->getUrl() );
3009 url.setPath( *it );
3010 kmkernel->iCalIface().folderDeletedOnServer( url );
3011 }
3012 serverSyncInternal();
3013}
3014
3015int KMFolderCachedImap::createIndexFromContentsRecursive()
3016{
3017 if ( !folder() || !folder()->child() )
3018 return 0;
3019
3020 KMFolderNode *node = 0;
3021 for( TQPtrListIterator<KMFolderNode> it( *folder()->child() ); (node = it.current()); ++it ) {
3022 if( !node->isDir() ) {
3023 KMFolderCachedImap* storage = static_cast<KMFolderCachedImap*>(static_cast<KMFolder*>(node)->storage());
3024 kdDebug() << k_funcinfo << "Re-indexing: " << storage->folder()->label() << endl;
3025 int rv = storage->createIndexFromContentsRecursive();
3026 if ( rv > 0 )
3027 return rv;
3028 }
3029 }
3030
3031 return createIndexFromContents();
3032}
3033
3034void KMFolderCachedImap::setAlarmsBlocked( bool blocked )
3035{
3036 mAlarmsBlocked = blocked;
3037}
3038
3039bool KMFolderCachedImap::alarmsBlocked() const
3040{
3041 return mAlarmsBlocked;
3042}
3043
3044bool KMFolderCachedImap::isCloseToQuota() const
3045{
3046 bool closeToQuota = false;
3047 if ( mQuotaInfo.isValid() && mQuotaInfo.max().toInt() > 0 ) {
3048 const int ratio = mQuotaInfo.current().toInt() * 100 / mQuotaInfo.max().toInt();
3049 //kdDebug(5006) << "Quota ratio: " << ratio << "% " << mQuotaInfo.toString() << endl;
3050 closeToQuota = ( ratio > 0 && ratio >= GlobalSettings::closeToQuotaThreshold() );
3051 }
3052 //kdDebug(5006) << "Folder: " << folder()->prettyURL() << " is over quota: " << closeToQuota << endl;
3053 return closeToQuota;
3054}
3055
3056KMCommand* KMFolderCachedImap::rescueUnsyncedMessages()
3057{
3058 TQValueList<unsigned long> newMsgs = findNewMessages();
3059 kdDebug() << k_funcinfo << newMsgs << " of " << count() << endl;
3060 if ( newMsgs.isEmpty() )
3061 return 0;
3062 KMFolder *dest = 0;
3063 bool manualMove = true;
3064 while ( GlobalSettings::autoLostFoundMove() ) {
3065 // find the inbox of this account
3066 KMFolder *inboxFolder = kmkernel->findFolderById( TQString(".%1.directory/INBOX").arg( account()->id() ) );
3067 if ( !inboxFolder ) {
3068 kdWarning(5006) << k_funcinfo << "inbox not found!" << endl;
3069 break;
3070 }
3071 KMFolderDir *inboxDir = inboxFolder->child();
3072 if ( !inboxDir && !inboxFolder->storage() )
3073 break;
3074 assert( inboxFolder->storage()->folderType() == KMFolderTypeCachedImap );
3075
3076 // create lost+found folder if needed
3077 KMFolderNode *node;
3078 KMFolder *lfFolder = 0;
3079 if ( !(node = inboxDir->hasNamedFolder( i18n("lost+found") )) ) {
3080 kdDebug(5006) << k_funcinfo << "creating lost+found folder" << endl;
3081 KMFolder* folder = kmkernel->dimapFolderMgr()->createFolder(
3082 i18n("lost+found"), false, KMFolderTypeCachedImap, inboxDir );
3083 if ( !folder || !folder->storage() )
3084 break;
3085 static_cast<KMFolderCachedImap*>( folder->storage() )->initializeFrom(
3086 static_cast<KMFolderCachedImap*>( inboxFolder->storage() ) );
3087 folder->storage()->setContentsType( KMail::ContentsTypeMail );
3088 folder->storage()->writeConfig();
3089 lfFolder = folder;
3090 } else {
3091 kdDebug(5006) << k_funcinfo << "found lost+found folder" << endl;
3092 lfFolder = dynamic_cast<KMFolder*>( node );
3093 }
3094 if ( !lfFolder || !lfFolder->createChildFolder() || !lfFolder->storage() )
3095 break;
3096
3097 // create subfolder for this incident
3098 TQDate today = TQDate::currentDate();
3099 TQString baseName = folder()->label() + "-" + TQString::number( today.year() )
3100 + (today.month() < 10 ? "0" : "" ) + TQString::number( today.month() )
3101 + (today.day() < 10 ? "0" : "" ) + TQString::number( today.day() );
3102 TQString name = baseName;
3103 int suffix = 0;
3104 while ( (node = lfFolder->child()->hasNamedFolder( name )) ) {
3105 ++suffix;
3106 name = baseName + '-' + TQString::number( suffix );
3107 }
3108 kdDebug(5006) << k_funcinfo << "creating lost+found folder " << name << endl;
3109 dest = kmkernel->dimapFolderMgr()->createFolder( name, false, KMFolderTypeCachedImap, lfFolder->child() );
3110 if ( !dest || !dest->storage() )
3111 break;
3112 static_cast<KMFolderCachedImap*>( dest->storage() )->initializeFrom(
3113 static_cast<KMFolderCachedImap*>( lfFolder->storage() ) );
3114 dest->storage()->setContentsType( contentsType() );
3115 dest->storage()->writeConfig();
3116
3117 KMessageBox::sorry( 0, i18n("<p>There are new messages in folder <b>%1</b>, which "
3118 "have not been uploaded to the server yet, but the folder has been deleted "
3119 "on the server or you do not "
3120 "have sufficient access rights on the folder to upload them.</p>"
3121 "<p>All affected messages will therefore be moved to <b>%2</b> "
3122 "to avoid data loss.</p>").arg( folder()->prettyURL() ).arg( dest->prettyURL() ),
3123 i18n("Insufficient access rights") );
3124 manualMove = false;
3125 break;
3126 }
3127
3128 if ( manualMove ) {
3129 const TQString msg ( i18n( "<p>There are new messages in this folder (%1), which "
3130 "have not been uploaded to the server yet, but the folder has been deleted "
3131 "on the server or you do not "
3132 "have sufficient access rights on the folder now to upload them. "
3133 "Please contact your administrator to allow upload of new messages "
3134 "to you, or move them out of this folder.</p> "
3135 "<p>Do you want to move these messages to another folder now?</p>").arg( folder()->prettyURL() ) );
3136 if ( KMessageBox::warningYesNo( 0, msg, TQString(), i18n("Move"), i18n("Do Not Move") ) == KMessageBox::Yes ) {
3137 KMail::KMFolderSelDlg dlg( kmkernel->getKMMainWidget(),
3138 i18n("Move Messages to Folder"), true );
3139 if ( dlg.exec() ) {
3140 dest = dlg.folder();
3141 }
3142 }
3143 }
3144 if ( dest ) {
3145 TQPtrList<KMMsgBase> msgs;
3146 for( int i = 0; i < count(); ++i ) {
3147 KMMsgBase *msg = getMsgBase( i );
3148 if( !msg ) continue; /* what goes on if getMsg() returns 0? */
3149 if ( msg->UID() == 0 )
3150 msgs.append( msg );
3151 }
3152 KMCommand *command = new KMMoveCommand( dest, msgs );
3153 command->start();
3154 return command;
3155 }
3156 return 0;
3157}
3158
3159void KMFolderCachedImap::rescueUnsyncedMessagesAndDeleteFolder( KMFolder *folder, bool root )
3160{
3161 kdDebug() << k_funcinfo << folder << " " << root << endl;
3162 if ( root )
3163 mToBeDeletedAfterRescue.append( folder );
3164 folder->open("cachedimap");
3165 KMFolderCachedImap* storage = dynamic_cast<KMFolderCachedImap*>( folder->storage() );
3166 if ( storage ) {
3167 KMCommand *command = storage->rescueUnsyncedMessages();
3168 if ( command ) {
3169 connect( command, TQ_SIGNAL(completed(KMCommand*)),
3170 TQ_SLOT(slotRescueDone(KMCommand*)) );
3171 ++mRescueCommandCount;
3172 } else {
3173 // nothing to rescue, close folder
3174 // (we don't need to close it in the other case, it will be deleted anyway)
3175 folder->close("cachedimap");
3176 }
3177 }
3178 if ( folder->child() ) {
3179 KMFolderNode *node = folder->child()->first();
3180 while (node) {
3181 if (!node->isDir() ) {
3182 KMFolder *subFolder = static_cast<KMFolder*>( node );
3183 rescueUnsyncedMessagesAndDeleteFolder( subFolder, false );
3184 }
3185 node = folder->child()->next();
3186 }
3187 }
3188}
3189
3190void KMFolderCachedImap::slotRescueDone(KMCommand * command)
3191{
3192 // FIXME: check command result
3193 if ( command )
3194 --mRescueCommandCount;
3195 if ( mRescueCommandCount > 0 )
3196 return;
3197 for ( TQValueList<KMFolder*>::ConstIterator it = mToBeDeletedAfterRescue.constBegin();
3198 it != mToBeDeletedAfterRescue.constEnd(); ++it ) {
3199 kmkernel->dimapFolderMgr()->remove( *it );
3200 }
3201 mToBeDeletedAfterRescue.clear();
3202 serverSyncInternal();
3203}
3204
3205void KMFolderCachedImap::slotRenameFolderFinished()
3206{
3207 // The syncing code assumes the folder was opened by us, and later closes it. So better
3208 // make sure the reference count is correct, since the folder was force-closed by the rename.
3209 // Otherwise bad things can happen, see https://issues.kolab.org/issue3853.
3210 open( "cachedimap" );
3211 serverSyncInternal();
3212}
3213
3214bool KMFolderCachedImap::canDeleteMessages() const
3215{
3216 if ( isReadOnly() )
3217 return false;
3218 if ( mUserRightsState == KMail::ACLJobs::Ok && !(userRights() & ACLJobs::Delete) )
3219 return false;
3220 return true;
3221}
3222
3223bool KMFolderCachedImap::mailCheckInProgress() const
3224{
3225 return mSyncState != SYNC_STATE_INITIAL;
3226}
3227
3228#include "kmfoldercachedimap.moc"
virtual void setContentsType(KMail::FolderContentsType type, bool quiet=false)
Set the type of contents held in this folder (mail, calendar, etc.)
virtual void readConfig()
Read the config file.
virtual void setStatus(int idx, KMMsgStatus status, bool toggle=false)
Set the status of the message at index idx to status.
virtual KMFolderType folderType() const
Returns the type of this folder.
Definition: folderstorage.h:96
virtual void remove()
Removes the folder physically from disk and empties the contents of the folder in memory.
virtual void writeConfig()
Write the config file.
virtual int addMsg(TQPtrList< KMMessage > &, TQValueList< int > &index_return)
Adds the given messages to the folder.
KMail list that manages the contents of one directory that may contain folders and/or other directori...
Definition: kmfolderdir.h:16
virtual KMFolder * createFolder(const TQString &folderName, bool sysFldr=false, KMFolderType folderType=KMFolderTypeMbox)
Create a mail folder in this directory with given name.
Definition: kmfolderdir.cpp:95
virtual KMFolderNode * hasNamedFolder(const TQString &name)
Returns folder with given name or zero if it does not exist.
TQString label() const
Returns the label of the folder for visualization.
RAII for KMFolder::open() / close().
Definition: kmfolder.h:688
Mail folder.
Definition: kmfolder.h:69
TQString idString() const
Returns a string that can be used to identify this folder.
Definition: kmfolder.cpp:705
virtual TQString prettyURL() const
URL of the node for visualization purposes.
Definition: kmfolder.cpp:593
virtual TQString label() const
Returns the label of the folder for visualization.
Definition: kmfolder.cpp:581
void close(const char *owner, bool force=false)
Close folder.
Definition: kmfolder.cpp:489
KMFolderDir * createChildFolder()
Create a child folder directory and associates it with this folder.
Definition: kmfolder.cpp:264
void setNoContent(bool aNoContent)
Specify, that the folder can't contain mails.
Definition: kmfolder.cpp:306
int open(const char *owner)
Open folder for access.
Definition: kmfolder.cpp:479
KMFolderDir * child() const
Returns the folder directory associated with this node or 0 if no such directory exists.
Definition: kmfolder.h:157
This is a Mime Message.
Definition: kmmessage.h:68
ulong UID() const
Get/set UID.
Definition: kmmessage.cpp:2225
The account manager is responsible for creating accounts of various types via the factory method crea...
Generic folder list job for (d)imap accounts.
Definition: listjob.h:55
void setHonorLocalSubscription(bool value)
Set whether the listing should include only folders that the account is subscribed to locally.
Definition: listjob.cpp:245
void setNamespace(const TQString &ns)
Set the namespace for this listing.
Definition: listjob.h:105
@ NotFetchedYet
The user rights/ACL have not been fetched from the server yet, we don't know them.
Definition: acljobs.h:65
@ Ok
The user rights/ACL have been fetched from the server sucessfully.
Definition: acljobs.h:66
@ FetchFailed
The attempt to fetch the user rights/ACL from the server failed.
Definition: acljobs.h:67
MultiSetACLJob * multiSetACL(TDEIO::Slave *slave, const KURL &url, const ACLList &acl)
Set and delete a list of permissions for different users on a given url.
Definition: acljobs.cpp:258
GetStorageQuotaJob * getStorageQuota(TDEIO::Slave *slave, const KURL &url)
Get the storage quota for a mailbox, if there is one.
Definition: quotajobs.cpp:92
folderdiaquotatab.h
Definition: aboutdata.cpp:40
One entry in the annotation list: attribute name and attribute value.
Information about a message to be downloaded (from the 'IMAP envelope')
Definition: cachedimapjob.h:66